Merge branch '2023-10-12-expo-add-support-for-edting-lines-of-text'
To quote the author:
So far expo only supports menus. These are quite flexible for various
kinds of settings, but cannot deal with free-form input, such as a
serial number or a machine name.
This series adds support for a textline object, which is a single line
of text. It has a maximum length and its value is stored within the expo
structure.
U-Boot already has a command-line editor which provides most of the
features needed by expo. But the code runs in its own loop and only
returns when the line is finished. This is not suitable for expo, which
must handle a keypress at a time, returning to its caller after each
one.
In order to use the CLI code, some significant refactoring is included
here. This mostly involves moving the internal loop of the CLI to a
separate function and recording its state in a struct, just as was done
for single keypresses some time back. A minor addition is support for
Ctrl-W to delete a word, since strangely this is currently only present
in the simple version.
The video-console system provides most of the features needed by
testline, but a few things are missing. This series provides:
- primitive cursor support so the user can see where he is typing
- saving and restoring of the text-entry context, so that expo can allow
the user to continue where he left off, including deleting previously
entered characters correctly (for Truetype)
- obtaining the nominal width of a string of n characters, so that a
suitable width can be chosen for the textline object
Note that no support is provided for clearing the cursor. This was
addressed in a previous series[1] which could perhaps be rebased. For
this implementation, the cursor is therefore not enabled for the normal
command line, only for expo.
Reading and writing textline objects is supported for FDT and
environment, but not for CMOS RAM, since it would likely use too much
RAM to store a string.
In terms of code size, the overall size increase is 180 bytes for
Thumb02 boards, 160 of whcih is the addition of Ctrl-W to delete a word.
[1] https://patchwork.ozlabs.org/project/uboot/list/?series=280178&state=*
diff --git a/arch/sandbox/dts/cedit.dtsi b/arch/sandbox/dts/cedit.dtsi
index a9eb4c2..9bd84e6 100644
--- a/arch/sandbox/dts/cedit.dtsi
+++ b/arch/sandbox/dts/cedit.dtsi
@@ -51,6 +51,14 @@
item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
};
+
+ machine-name {
+ id = <ID_MACHINE_NAME>;
+ type = "textline";
+ max-chars = <20>;
+ title = "Machine name";
+ edit-id = <ID_MACHINE_NAME_EDIT>;
+ };
};
};
diff --git a/boot/Makefile b/boot/Makefile
index 6ce983b..ad60859 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -56,7 +56,8 @@
obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o
endif
-obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o scene_menu.o expo_build.o
+obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo.o scene.o expo_build.o
+obj-$(CONFIG_$(SPL_TPL_)EXPO) += scene_menu.o scene_textline.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_REQUEST) += vbe_request.o
diff --git a/boot/cedit.c b/boot/cedit.c
index 73645f7..8c654db 100644
--- a/boot/cedit.c
+++ b/boot/cedit.c
@@ -71,10 +71,22 @@
y = 100;
list_for_each_entry(obj, &scn->obj_head, sibling) {
- if (obj->type == SCENEOBJT_MENU) {
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_MENU:
scene_obj_set_pos(scn, obj->id, 50, y);
scene_menu_arrange(scn, (struct scene_obj_menu *)obj);
y += 50;
+ break;
+ case SCENEOBJT_TEXTLINE:
+ scene_obj_set_pos(scn, obj->id, 50, y);
+ scene_textline_arrange(scn,
+ (struct scene_obj_textline *)obj);
+ y += 50;
+ break;
}
}
@@ -170,7 +182,7 @@
key = 0;
if (ichar) {
key = bootmenu_conv_key(ichar);
- if (key == BKEY_NONE)
+ if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA)
key = ichar;
}
if (!key)
@@ -229,6 +241,16 @@
return 0;
}
+/**
+ * get_cur_menuitem_text() - Get the text of the currently selected item
+ *
+ * Looks up the object for the current item, finds text object for it and looks
+ * up the string for that text
+ *
+ * @menu: Menu to look at
+ * @strp: Returns a pointer to the next
+ * Return: 0 if OK, -ENOENT if something was not found
+ */
static int get_cur_menuitem_text(const struct scene_obj_menu *menu,
const char **strp)
{
@@ -253,22 +275,55 @@
return 0;
}
+static int write_dt_string(struct abuf *buf, const char *name, const char *str)
+{
+ int ret, i;
+
+ /* write the text of the current item */
+ ret = -EAGAIN;
+ for (i = 0; ret && i < 2; i++) {
+ ret = fdt_property_string(abuf_data(buf), name, str);
+ if (!i) {
+ ret = check_space(ret, buf);
+ if (ret)
+ return log_msg_ret("rs2", -ENOMEM);
+ }
+ }
+
+ /* this should not happen */
+ if (ret)
+ return log_msg_ret("str", -EFAULT);
+
+ return 0;
+}
+
static int h_write_settings(struct scene_obj *obj, void *vpriv)
{
struct cedit_iter_priv *priv = vpriv;
struct abuf *buf = priv->buf;
+ int ret;
switch (obj->type) {
case SCENEOBJT_NONE:
case SCENEOBJT_IMAGE:
case SCENEOBJT_TEXT:
break;
+ case SCENEOBJT_TEXTLINE: {
+ const struct scene_obj_textline *tline;
+
+ tline = (struct scene_obj_textline *)obj;
+ ret = write_dt_string(buf, obj->name, abuf_data(&tline->buf));
+ if (ret)
+ return log_msg_ret("wr2", ret);
+ break;
+ }
case SCENEOBJT_MENU: {
const struct scene_obj_menu *menu;
const char *str;
char name[80];
- int ret, i;
+ int i;
+ /* write the ID of the current item */
menu = (struct scene_obj_menu *)obj;
ret = -EAGAIN;
for (i = 0; ret && i < 2; i++) {
@@ -288,20 +343,11 @@
if (ret)
return log_msg_ret("mis", ret);
+ /* write the text of the current item */
snprintf(name, sizeof(name), "%s-str", obj->name);
- ret = -EAGAIN;
- for (i = 0; ret && i < 2; i++) {
- ret = fdt_property_string(abuf_data(buf), name, str);
- if (!i) {
- ret = check_space(ret, buf);
- if (ret)
- return log_msg_ret("rs2", -ENOMEM);
- }
- }
-
- /* this should not happen */
+ ret = write_dt_string(buf, name, str);
if (ret)
- return log_msg_ret("wr2", -EFAULT);
+ return log_msg_ret("wr2", ret);
break;
}
@@ -364,6 +410,19 @@
case SCENEOBJT_IMAGE:
case SCENEOBJT_TEXT:
break;
+ case SCENEOBJT_TEXTLINE: {
+ const struct scene_obj_textline *tline;
+ const char *val;
+ int len;
+
+ tline = (struct scene_obj_textline *)obj;
+
+ val = ofnode_read_prop(node, obj->name, &len);
+ if (len >= tline->max_chars)
+ return log_msg_ret("str", -ENOSPC);
+ strcpy(abuf_data(&tline->buf), val);
+ break;
+ }
case SCENEOBJT_MENU: {
struct scene_obj_menu *menu;
uint val;
@@ -412,31 +471,51 @@
const char *str;
int val, ret;
- if (obj->type != SCENEOBJT_MENU)
- return 0;
-
- menu = (struct scene_obj_menu *)obj;
- val = menu->cur_item_id;
snprintf(var, sizeof(var), "c.%s", obj->name);
- if (priv->verbose)
- printf("%s=%d\n", var, val);
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_MENU:
+ menu = (struct scene_obj_menu *)obj;
+ val = menu->cur_item_id;
- ret = env_set_ulong(var, val);
- if (ret)
- return log_msg_ret("set", ret);
+ if (priv->verbose)
+ printf("%s=%d\n", var, val);
- ret = get_cur_menuitem_text(menu, &str);
- if (ret)
- return log_msg_ret("mis", ret);
+ ret = env_set_ulong(var, val);
+ if (ret)
+ return log_msg_ret("set", ret);
+
+ ret = get_cur_menuitem_text(menu, &str);
+ if (ret)
+ return log_msg_ret("mis", ret);
- snprintf(name, sizeof(name), "c.%s-str", obj->name);
- if (priv->verbose)
- printf("%s=%s\n", name, str);
+ snprintf(name, sizeof(name), "c.%s-str", obj->name);
+ if (priv->verbose)
+ printf("%s=%s\n", name, str);
- ret = env_set(name, str);
- if (ret)
- return log_msg_ret("st2", ret);
+ ret = env_set(name, str);
+ if (ret)
+ return log_msg_ret("st2", ret);
+ break;
+ case SCENEOBJT_TEXTLINE: {
+ const struct scene_obj_textline *tline;
+
+ tline = (struct scene_obj_textline *)obj;
+ str = abuf_data(&tline->buf);
+ ret = env_set(var, str);
+ if (ret)
+ return log_msg_ret("set", ret);
+
+ if (priv->verbose)
+ printf("%s=%s\n", var, str);
+
+ break;
+ }
+ }
return 0;
}
@@ -464,24 +543,43 @@
char var[60];
int val;
- if (obj->type != SCENEOBJT_MENU)
- return 0;
-
- menu = (struct scene_obj_menu *)obj;
- val = menu->cur_item_id;
snprintf(var, sizeof(var), "c.%s", obj->name);
- val = env_get_ulong(var, 10, 0);
- if (priv->verbose)
- printf("%s=%d\n", var, val);
- if (!val)
- return log_msg_ret("get", -ENOENT);
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_MENU:
+ menu = (struct scene_obj_menu *)obj;
+ val = env_get_ulong(var, 10, 0);
+ if (priv->verbose)
+ printf("%s=%d\n", var, val);
+ if (!val)
+ return log_msg_ret("get", -ENOENT);
+
+ /*
+ * note that no validation is done here, to make sure the ID is
+ * valid * and actually points to a menu item
+ */
+ menu->cur_item_id = val;
+ break;
+ case SCENEOBJT_TEXTLINE: {
+ const struct scene_obj_textline *tline;
+ const char *value;
- /*
- * note that no validation is done here, to make sure the ID is valid
- * and actually points to a menu item
- */
- menu->cur_item_id = val;
+ tline = (struct scene_obj_textline *)obj;
+ value = env_get(var);
+ if (value && strlen(value) >= tline->max_chars)
+ return log_msg_ret("str", -ENOSPC);
+ if (!value)
+ value = "";
+ if (priv->verbose)
+ printf("%s=%s\n", var, value);
+ strcpy(abuf_data(&tline->buf), value);
+ break;
+ }
+ }
return 0;
}
diff --git a/boot/expo_build.c b/boot/expo_build.c
index 910f1b4..04d88a2 100644
--- a/boot/expo_build.c
+++ b/boot/expo_build.c
@@ -23,10 +23,14 @@
* if there is nothing for this ID. Since ID 0 is never used, the first
* element of this array is always NULL
* @str_count: Number of entries in @str_for_id
+ * @err_node: Node being processed (for error reporting)
+ * @err_prop: Property being processed (for error reporting)
*/
struct build_info {
const char **str_for_id;
int str_count;
+ ofnode err_node;
+ const char *err_prop;
};
/**
@@ -46,6 +50,7 @@
uint str_id;
int ret;
+ info->err_prop = find_name;
text = ofnode_read_string(node, find_name);
if (!text) {
char name[40];
@@ -54,7 +59,7 @@
snprintf(name, sizeof(name), "%s-id", find_name);
ret = ofnode_read_u32(node, name, &id);
if (ret)
- return log_msg_ret("id", -EINVAL);
+ return log_msg_ret("id", -ENOENT);
if (id >= info->str_count)
return log_msg_ret("id", -E2BIG);
@@ -164,9 +169,10 @@
int ret;
u32 id;
+ info->err_node = node;
ret = ofnode_read_u32(node, "id", &id);
if (ret)
- return log_msg_ret("id", -EINVAL);
+ return log_msg_ret("id", -ENOENT);
val = ofnode_read_string(node, "value");
if (!val)
return log_msg_ret("val", -EINVAL);
@@ -241,6 +247,8 @@
return log_msg_ret("tit", ret);
title_id = ret;
ret = scene_menu_set_title(scn, menu_id, title_id);
+ if (ret)
+ return log_msg_ret("set", ret);
item_ids = ofnode_read_prop(node, "item-id", &size);
if (!item_ids)
@@ -279,6 +287,49 @@
return 0;
}
+static int textline_build(struct build_info *info, ofnode node,
+ struct scene *scn, uint id, struct scene_obj **objp)
+{
+ struct scene_obj_textline *ted;
+ uint ted_id, edit_id;
+ const char *name;
+ u32 max_chars;
+ int ret;
+
+ name = ofnode_get_name(node);
+
+ info->err_prop = "max-chars";
+ ret = ofnode_read_u32(node, "max-chars", &max_chars);
+ if (ret)
+ return log_msg_ret("max", -ENOENT);
+
+ ret = scene_textline(scn, name, id, max_chars, &ted);
+ if (ret < 0)
+ return log_msg_ret("ted", ret);
+ ted_id = ret;
+
+ /* Set the title */
+ ret = add_txt_str(info, node, scn, "title", 0);
+ if (ret < 0)
+ return log_msg_ret("tit", ret);
+ ted->label_id = ret;
+
+ /* Setup the editor */
+ info->err_prop = "edit-id";
+ ret = ofnode_read_u32(node, "edit-id", &id);
+ if (ret)
+ return log_msg_ret("id", -ENOENT);
+ edit_id = ret;
+
+ ret = scene_txt_str(scn, "edit", edit_id, 0, abuf_data(&ted->buf),
+ NULL);
+ if (ret < 0)
+ return log_msg_ret("add", ret);
+ ted->edit_id = ret;
+
+ return 0;
+}
+
/**
* obj_build() - Build an expo object and add it to a scene
*
@@ -300,7 +351,7 @@
log_debug("- object %s\n", ofnode_get_name(node));
ret = ofnode_read_u32(node, "id", &id);
if (ret)
- return log_msg_ret("id", -EINVAL);
+ return log_msg_ret("id", -ENOENT);
type = ofnode_read_string(node, "type");
if (!type)
@@ -308,8 +359,10 @@
if (!strcmp("menu", type))
ret = menu_build(info, node, scn, id, &obj);
- else
- ret = -EINVAL;
+ else if (!strcmp("textline", type))
+ ret = textline_build(info, node, scn, id, &obj);
+ else
+ ret = -EOPNOTSUPP;
if (ret)
return log_msg_ret("bld", ret);
@@ -341,11 +394,12 @@
ofnode node;
int ret;
+ info->err_node = scn_node;
name = ofnode_get_name(scn_node);
log_debug("Building scene %s\n", name);
ret = ofnode_read_u32(scn_node, "id", &id);
if (ret)
- return log_msg_ret("id", -EINVAL);
+ return log_msg_ret("id", -ENOENT);
ret = scene_new(exp, name, id, &scn);
if (ret < 0)
@@ -362,6 +416,7 @@
return log_msg_ret("pr", ret);
ofnode_for_each_subnode(node, scn_node) {
+ info->err_node = node;
ret = obj_build(info, node, scn);
if (ret < 0)
return log_msg_ret("mit", ret);
@@ -370,20 +425,19 @@
return 0;
}
-int expo_build(ofnode root, struct expo **expp)
+int build_it(struct build_info *info, ofnode root, struct expo **expp)
{
- struct build_info info;
ofnode scenes, node;
struct expo *exp;
u32 dyn_start;
int ret;
- memset(&info, '\0', sizeof(info));
- ret = read_strings(&info, root);
+ ret = read_strings(info, root);
if (ret)
return log_msg_ret("str", ret);
if (_DEBUG)
- list_strings(&info);
+ list_strings(info);
+ info->err_node = root;
ret = expo_new("name", NULL, &exp);
if (ret)
@@ -397,7 +451,7 @@
return log_msg_ret("sno", -EINVAL);
ofnode_for_each_subnode(node, scenes) {
- ret = scene_build(&info, node, exp);
+ ret = scene_build(info, node, exp);
if (ret < 0)
return log_msg_ret("scn", ret);
}
@@ -405,3 +459,27 @@
return 0;
}
+
+int expo_build(ofnode root, struct expo **expp)
+{
+ struct build_info info;
+ struct expo *exp;
+ int ret;
+
+ memset(&info, '\0', sizeof(info));
+ ret = build_it(&info, root, &exp);
+ if (ret) {
+ char buf[120];
+ int node_ret;
+
+ node_ret = ofnode_get_path(info.err_node, buf, sizeof(buf));
+ log_warning("Build failed at node %s, property %s\n",
+ node_ret ? ofnode_get_name(info.err_node) : buf,
+ info.err_prop);
+
+ return log_msg_ret("bui", ret);
+ }
+ *expp = exp;
+
+ return 0;
+}
diff --git a/boot/scene.c b/boot/scene.c
index 6c52948..d4dfb49 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -32,6 +32,14 @@
return log_msg_ret("name", -ENOMEM);
}
+ abuf_init(&scn->buf);
+ if (!abuf_realloc(&scn->buf, EXPO_MAX_CHARS + 1)) {
+ free(scn->name);
+ free(scn);
+ return log_msg_ret("buf", -ENOMEM);
+ }
+ abuf_init(&scn->entry_save);
+
INIT_LIST_HEAD(&scn->obj_head);
scn->id = resolve_id(exp, id);
scn->expo = exp;
@@ -57,6 +65,8 @@
list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
scene_obj_destroy(obj);
+ abuf_uninit(&scn->entry_save);
+ abuf_uninit(&scn->buf);
free(scn->name);
free(scn);
}
@@ -137,7 +147,7 @@
sizeof(struct scene_obj_img),
(struct scene_obj **)&img);
if (ret < 0)
- return log_msg_ret("obj", -ENOMEM);
+ return log_msg_ret("obj", ret);
img->data = data;
@@ -157,7 +167,7 @@
sizeof(struct scene_obj_txt),
(struct scene_obj **)&txt);
if (ret < 0)
- return log_msg_ret("obj", -ENOMEM);
+ return log_msg_ret("obj", ret);
txt->str_id = str_id;
@@ -176,14 +186,15 @@
ret = expo_str(scn->expo, name, str_id, str);
if (ret < 0)
return log_msg_ret("str", ret);
- else if (ret != str_id)
+ 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", -ENOMEM);
+ return log_msg_ret("obj", ret);
txt->str_id = str_id;
@@ -269,6 +280,7 @@
switch (obj->type) {
case SCENEOBJT_NONE:
case SCENEOBJT_MENU:
+ case SCENEOBJT_TEXTLINE:
break;
case SCENEOBJT_IMAGE: {
struct scene_obj_img *img = (struct scene_obj_img *)obj;
@@ -314,6 +326,51 @@
}
/**
+ * scene_render_background() - Render the background for an object
+ *
+ * @obj: Object to render
+ * @box_only: true to show a box around the object, but keep the normal
+ * background colour inside
+ */
+static void scene_render_background(struct scene_obj *obj, bool box_only)
+{
+ struct expo *exp = obj->scene->expo;
+ const struct expo_theme *theme = &exp->theme;
+ struct vidconsole_bbox bbox, label_bbox;
+ struct udevice *dev = exp->display;
+ struct video_priv *vid_priv;
+ struct udevice *cons = exp->cons;
+ struct vidconsole_colour old;
+ enum colour_idx fore, back;
+ uint inset = theme->menu_inset;
+
+ /* draw a background for the object */
+ if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
+ fore = VID_BLACK;
+ back = VID_WHITE;
+ } else {
+ fore = VID_LIGHT_GRAY;
+ back = VID_BLACK;
+ }
+
+ /* see if this object wants to render a background */
+ if (scene_obj_calc_bbox(obj, &bbox, &label_bbox))
+ return;
+
+ vidconsole_push_colour(cons, fore, back, &old);
+ vid_priv = dev_get_uclass_priv(dev);
+ video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
+ label_bbox.x1 + inset, label_bbox.y1 + inset,
+ vid_priv->colour_fg);
+ vidconsole_pop_colour(cons, &old);
+ if (box_only) {
+ video_fill_part(dev, label_bbox.x0, label_bbox.y0,
+ label_bbox.x1, label_bbox.y1,
+ vid_priv->colour_bg);
+ }
+}
+
+/**
* scene_obj_render() - Render an object
*
*/
@@ -396,7 +453,7 @@
return -ENOTSUPP;
/* draw a background behind the menu items */
- scene_menu_render(menu);
+ scene_render_background(obj, false);
}
/*
* With a vidconsole, the text and item pointer are rendered as
@@ -412,6 +469,10 @@
break;
}
+ case SCENEOBJT_TEXTLINE:
+ if (obj->flags & SCENEOF_OPEN)
+ scene_render_background(obj, true);
+ break;
}
return 0;
@@ -423,13 +484,29 @@
int ret;
list_for_each_entry(obj, &scn->obj_head, sibling) {
- if (obj->type == SCENEOBJT_MENU) {
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_MENU: {
struct scene_obj_menu *menu;
menu = (struct scene_obj_menu *)obj,
ret = scene_menu_arrange(scn, menu);
if (ret)
return log_msg_ret("arr", ret);
+ break;
+ }
+ case SCENEOBJT_TEXTLINE: {
+ struct scene_obj_textline *tline;
+
+ tline = (struct scene_obj_textline *)obj,
+ ret = scene_textline_arrange(scn, tline);
+ if (ret)
+ return log_msg_ret("arr", ret);
+ break;
+ }
}
}
@@ -452,9 +529,20 @@
if (ret && ret != -ENOTSUPP)
return log_msg_ret("ren", ret);
- if (obj->type == SCENEOBJT_MENU)
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_MENU:
scene_menu_render_deps(scn,
(struct scene_obj_menu *)obj);
+ break;
+ case SCENEOBJT_TEXTLINE:
+ scene_textline_render_deps(scn,
+ (struct scene_obj_textline *)obj);
+ break;
+ }
}
return 0;
@@ -501,7 +589,7 @@
sibling)) {
obj = list_entry(obj->sibling.prev,
struct scene_obj, sibling);
- if (obj->type == SCENEOBJT_MENU) {
+ if (scene_obj_can_highlight(obj)) {
event->type = EXPOACT_POINT_OBJ;
event->select.id = obj->id;
log_debug("up to obj %d\n", event->select.id);
@@ -513,7 +601,7 @@
while (!list_is_last(&obj->sibling, &scn->obj_head)) {
obj = list_entry(obj->sibling.next, struct scene_obj,
sibling);
- if (obj->type == SCENEOBJT_MENU) {
+ if (scene_obj_can_highlight(obj)) {
event->type = EXPOACT_POINT_OBJ;
event->select.id = obj->id;
log_debug("down to obj %d\n", event->select.id);
@@ -522,7 +610,7 @@
}
break;
case BKEY_SELECT:
- if (obj->type == SCENEOBJT_MENU) {
+ if (scene_obj_can_highlight(obj)) {
event->type = EXPOACT_OPEN;
event->select.id = obj->id;
log_debug("open obj %d\n", event->select.id);
@@ -537,7 +625,6 @@
int scene_send_key(struct scene *scn, int key, struct expo_action *event)
{
- struct scene_obj_menu *menu;
struct scene_obj *obj;
int ret;
@@ -561,10 +648,30 @@
return 0;
}
- menu = (struct scene_obj_menu *)obj,
- ret = scene_menu_send_key(scn, menu, key, event);
- if (ret)
- return log_msg_ret("key", ret);
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_MENU: {
+ struct scene_obj_menu *menu;
+
+ menu = (struct scene_obj_menu *)obj,
+ ret = scene_menu_send_key(scn, menu, key, event);
+ if (ret)
+ return log_msg_ret("key", ret);
+ break;
+ }
+ case SCENEOBJT_TEXTLINE: {
+ struct scene_obj_textline *tline;
+
+ tline = (struct scene_obj_textline *)obj,
+ ret = scene_textline_send_key(scn, tline, key, event);
+ if (ret)
+ return log_msg_ret("key", ret);
+ break;
+ }
+ }
return 0;
}
@@ -583,6 +690,32 @@
return 0;
}
+int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
+ struct vidconsole_bbox *label_bbox)
+{
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_TEXT:
+ return -ENOSYS;
+ case SCENEOBJT_MENU: {
+ struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
+
+ scene_menu_calc_bbox(menu, bbox, label_bbox);
+ break;
+ }
+ case SCENEOBJT_TEXTLINE: {
+ struct scene_obj_textline *tline;
+
+ tline = (struct scene_obj_textline *)obj;
+ scene_textline_calc_bbox(tline, bbox, label_bbox);
+ break;
+ }
+ }
+
+ return 0;
+}
+
int scene_calc_dims(struct scene *scn, bool do_menus)
{
struct scene_obj *obj;
@@ -616,6 +749,16 @@
}
break;
}
+ case SCENEOBJT_TEXTLINE: {
+ struct scene_obj_textline *tline;
+
+ tline = (struct scene_obj_textline *)obj;
+ ret = scene_textline_calc_dims(tline);
+ if (ret)
+ return log_msg_ret("men", ret);
+
+ break;
+ }
}
}
@@ -635,6 +778,7 @@
case SCENEOBJT_NONE:
case SCENEOBJT_IMAGE:
case SCENEOBJT_MENU:
+ case SCENEOBJT_TEXTLINE:
break;
case SCENEOBJT_TEXT:
scene_txt_set_font(scn, obj->id, NULL,
@@ -660,20 +804,49 @@
struct scene_obj *obj;
list_for_each_entry(obj, &scn->obj_head, sibling) {
- switch (obj->type) {
- case SCENEOBJT_MENU:
+ if (scene_obj_can_highlight(obj)) {
scene_set_highlight_id(scn, obj->id);
return;
- default:
- break;
}
}
}
+static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
+{
+ int ret;
+
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_IMAGE:
+ case SCENEOBJT_MENU:
+ case SCENEOBJT_TEXT:
+ break;
+ case SCENEOBJT_TEXTLINE:
+ ret = scene_textline_open(scn,
+ (struct scene_obj_textline *)obj);
+ if (ret)
+ return log_msg_ret("op", ret);
+ break;
+ }
+
+ return 0;
+}
+
int scene_set_open(struct scene *scn, uint id, bool open)
{
+ struct scene_obj *obj;
int ret;
+ obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
+ if (!obj)
+ return log_msg_ret("find", -ENOENT);
+
+ if (open) {
+ ret = scene_obj_open(scn, obj);
+ if (ret)
+ return log_msg_ret("op", ret);
+ }
+
ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
open ? SCENEOF_OPEN : 0);
if (ret)
@@ -697,3 +870,29 @@
return 0;
}
+
+int scene_bbox_union(struct scene *scn, uint id, int inset,
+ struct vidconsole_bbox *bbox)
+{
+ struct scene_obj *obj;
+
+ if (!id)
+ return 0;
+ obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
+ if (!obj)
+ return log_msg_ret("obj", -ENOENT);
+ if (bbox->valid) {
+ bbox->x0 = min(bbox->x0, obj->dim.x - 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);
+ } 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->valid = true;
+ }
+
+ return 0;
+}
diff --git a/boot/scene_internal.h b/boot/scene_internal.h
index 695a907..e72202c 100644
--- a/boot/scene_internal.h
+++ b/boot/scene_internal.h
@@ -9,6 +9,8 @@
#ifndef __SCENE_INTERNAL_H
#define __SCENE_INTERNAL_H
+struct vidconsole_bbox;
+
typedef int (*expo_scene_obj_iterator)(struct scene_obj *obj, void *priv);
/**
@@ -100,6 +102,18 @@
int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu);
/**
+ * scene_textline_arrange() - Set the position of things in a textline
+ *
+ * This updates any items associated with a textline to make sure they are
+ * positioned correctly relative to the textline.
+ *
+ * @scn: Scene to update
+ * @tline: textline to process
+ * Returns: 0 if OK, -ve on error
+ */
+int scene_textline_arrange(struct scene *scn, struct scene_obj_textline *tline);
+
+/**
* scene_apply_theme() - Apply a theme to a scene
*
* @scn: Scene to update
@@ -122,6 +136,18 @@
struct expo_action *event);
/**
+ * scene_textline_send_key() - Send a key to a textline for processing
+ *
+ * @scn: Scene to use
+ * @tline: textline to use
+ * @key: Key code to send (KEY_...)
+ * @event: Place to put any event which is generated by the key
+ * Returns: 0 if OK (always)
+ */
+int scene_textline_send_key(struct scene *scn, struct scene_obj_textline *tline,
+ int key, struct expo_action *event);
+
+/**
* scene_menu_destroy() - Destroy a menu in a scene
*
* @scn: Scene to destroy
@@ -164,13 +190,6 @@
int scene_send_key(struct scene *scn, int key, struct expo_action *event);
/**
- * scene_menu_render() - Render the background behind a menu
- *
- * @menu: Menu to render
- */
-void scene_menu_render(struct scene_obj_menu *menu);
-
-/**
* scene_render_deps() - Render an object and its dependencies
*
* @scn: Scene to render
@@ -185,12 +204,24 @@
* Renders the menu and all of its attached objects
*
* @scn: Scene to render
- * @menu: Menu render
+ * @menu: Menu to render
* Returns: 0 if OK, -ve on error
*/
int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu);
/**
+ * scene_textline_render_deps() - Render a textline and its dependencies
+ *
+ * Renders the textline and all of its attached objects
+ *
+ * @scn: Scene to render
+ * @tline: textline to render
+ * Returns: 0 if OK, -ve on error
+ */
+int scene_textline_render_deps(struct scene *scn,
+ struct scene_obj_textline *tline);
+
+/**
* scene_menu_calc_dims() - Calculate the dimensions of a menu
*
* Updates the width and height of the menu based on its contents
@@ -246,4 +277,85 @@
struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu,
uint seq);
+/**
+ * scene_bbox_union() - update bouding box with the demensions of an object
+ *
+ * Updates @bbox so that it encompasses the bounding box of object @id
+ *
+ * @snd: Scene containing object
+ * @id: Object id
+ * @inset: Amount of inset to use for width
+ * @bbox: Bounding box to update
+ * Return: 0 if OK, -ve on error
+ */
+int scene_bbox_union(struct scene *scn, uint id, int inset,
+ struct vidconsole_bbox *bbox);
+
+/**
+ * scene_textline_calc_dims() - Calculate the dimensions of a textline
+ *
+ * Updates the width and height of the textline based on its contents
+ *
+ * @tline: Textline to update
+ * Returns 0 if OK, -ENOTSUPP if there is no graphical console
+ */
+int scene_textline_calc_dims(struct scene_obj_textline *tline);
+
+/**
+ * scene_menu_calc_bbox() - Calculate bounding boxes for the menu
+ *
+ * @menu: Menu to process
+ * @bbox: Returns bounding box of menu including prompts
+ * @label_bbox: Returns bounding box of labels
+ * Return: 0 if OK, -ve on error
+ */
+void scene_menu_calc_bbox(struct scene_obj_menu *menu,
+ struct vidconsole_bbox *bbox,
+ struct vidconsole_bbox *label_bbox);
+
+/**
+ * scene_textline_calc_bbox() - Calculate bounding box for the textline
+ *
+ * @textline: Menu to process
+ * @bbox: Returns bounding box of textline including prompt
+ * @edit_bbox: Returns bounding box of editable part
+ * Return: 0 if OK, -ve on error
+ */
+void scene_textline_calc_bbox(struct scene_obj_textline *menu,
+ struct vidconsole_bbox *bbox,
+ struct vidconsole_bbox *label_bbox);
+
+/**
+ * scene_obj_calc_bbox() - Calculate bounding boxes for an object
+ *
+ * @obj: Object to process
+ * @bbox: Returns bounding box of object including prompts
+ * @label_bbox: Returns bounding box of labels (active area)
+ * Return: 0 if OK, -ve on error
+ */
+int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
+ struct vidconsole_bbox *label_bbox);
+
+/**
+ * scene_textline_open() - Open a textline object
+ *
+ * Set up the text editor ready for use
+ *
+ * @scn: Scene containing the textline
+ * @tline: textline object
+ * Return: 0 if OK, -ve on error
+ */
+int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline);
+
+/**
+ * scene_textline_close() - Close a textline object
+ *
+ * Close out the text editor after use
+ *
+ * @scn: Scene containing the textline
+ * @tline: textline object
+ * Return: 0 if OK, -ve on error
+ */
+int scene_textline_close(struct scene *scn, struct scene_obj_textline *tline);
+
#endif /* __SCENE_INTERNAL_H */
diff --git a/boot/scene_menu.c b/boot/scene_menu.c
index e0dcd0a..6399416 100644
--- a/boot/scene_menu.c
+++ b/boot/scene_menu.c
@@ -114,42 +114,9 @@
update_pointers(menu, item_id, true);
}
-static int scene_bbox_union(struct scene *scn, uint id, int inset,
- struct vidconsole_bbox *bbox)
-{
- struct scene_obj *obj;
-
- if (!id)
- return 0;
- obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
- if (!obj)
- return log_msg_ret("obj", -ENOENT);
- if (bbox->valid) {
- bbox->x0 = min(bbox->x0, obj->dim.x - 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);
- } 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->valid = true;
- }
-
- return 0;
-}
-
-/**
- * scene_menu_calc_bbox() - Calculate bounding boxes for the menu
- *
- * @menu: Menu to process
- * @bbox: Returns bounding box of menu including prompts
- * @label_bbox: Returns bounding box of labels
- */
-static void scene_menu_calc_bbox(struct scene_obj_menu *menu,
- struct vidconsole_bbox *bbox,
- struct vidconsole_bbox *label_bbox)
+void scene_menu_calc_bbox(struct scene_obj_menu *menu,
+ struct vidconsole_bbox *bbox,
+ struct vidconsole_bbox *label_bbox)
{
const struct expo_theme *theme = &menu->obj.scene->expo->theme;
const struct scene_menitem *item;
@@ -549,35 +516,6 @@
return -ENOTSUPP;
}
-void scene_menu_render(struct scene_obj_menu *menu)
-{
- struct expo *exp = menu->obj.scene->expo;
- const struct expo_theme *theme = &exp->theme;
- struct vidconsole_bbox bbox, label_bbox;
- struct udevice *dev = exp->display;
- struct video_priv *vid_priv;
- struct udevice *cons = exp->cons;
- struct vidconsole_colour old;
- enum colour_idx fore, back;
-
- if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
- fore = VID_BLACK;
- back = VID_WHITE;
- } else {
- fore = VID_LIGHT_GRAY;
- back = VID_BLACK;
- }
-
- scene_menu_calc_bbox(menu, &bbox, &label_bbox);
- vidconsole_push_colour(cons, fore, back, &old);
- vid_priv = dev_get_uclass_priv(dev);
- video_fill_part(dev, label_bbox.x0 - theme->menu_inset,
- label_bbox.y0 - theme->menu_inset,
- label_bbox.x1, label_bbox.y1 + theme->menu_inset,
- vid_priv->colour_fg);
- vidconsole_pop_colour(cons, &old);
-}
-
int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu)
{
struct scene_menitem *item;
diff --git a/boot/scene_textline.c b/boot/scene_textline.c
new file mode 100644
index 0000000..6ea072a
--- /dev/null
+++ b/boot/scene_textline.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Implementation of a menu in a scene
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY LOGC_EXPO
+
+#include <common.h>
+#include <expo.h>
+#include <menu.h>
+#include <video_console.h>
+#include "scene_internal.h"
+
+int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars,
+ struct scene_obj_textline **tlinep)
+{
+ struct scene_obj_textline *tline;
+ char *buf;
+ int ret;
+
+ if (max_chars >= EXPO_MAX_CHARS)
+ return log_msg_ret("chr", -E2BIG);
+
+ ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXTLINE,
+ sizeof(struct scene_obj_textline),
+ (struct scene_obj **)&tline);
+ if (ret < 0)
+ return log_msg_ret("obj", -ENOMEM);
+ abuf_init(&tline->buf);
+ if (!abuf_realloc(&tline->buf, max_chars + 1))
+ return log_msg_ret("buf", -ENOMEM);
+ buf = abuf_data(&tline->buf);
+ *buf = '\0';
+ tline->pos = max_chars;
+ tline->max_chars = max_chars;
+
+ if (tlinep)
+ *tlinep = tline;
+
+ return tline->obj.id;
+}
+
+void scene_textline_calc_bbox(struct scene_obj_textline *tline,
+ struct vidconsole_bbox *bbox,
+ struct vidconsole_bbox *edit_bbox)
+{
+ const struct expo_theme *theme = &tline->obj.scene->expo->theme;
+
+ bbox->valid = false;
+ scene_bbox_union(tline->obj.scene, tline->label_id, 0, bbox);
+ scene_bbox_union(tline->obj.scene, tline->edit_id, 0, bbox);
+
+ edit_bbox->valid = false;
+ scene_bbox_union(tline->obj.scene, tline->edit_id, theme->menu_inset,
+ edit_bbox);
+}
+
+int scene_textline_calc_dims(struct scene_obj_textline *tline)
+{
+ struct scene *scn = tline->obj.scene;
+ struct vidconsole_bbox bbox;
+ struct scene_obj_txt *txt;
+ int ret;
+
+ txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
+ if (!txt)
+ return log_msg_ret("dim", -ENOENT);
+
+ ret = vidconsole_nominal(scn->expo->cons, txt->font_name,
+ txt->font_size, tline->max_chars, &bbox);
+ if (ret)
+ 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;
+
+ scene_obj_set_size(scn, tline->edit_id, tline->obj.dim.w,
+ tline->obj.dim.h);
+ }
+
+ return 0;
+}
+
+int scene_textline_arrange(struct scene *scn, struct scene_obj_textline *tline)
+{
+ const bool open = tline->obj.flags & SCENEOF_OPEN;
+ bool point;
+ int x, y;
+ int ret;
+
+ x = tline->obj.dim.x;
+ y = tline->obj.dim.y;
+ if (tline->label_id) {
+ ret = scene_obj_set_pos(scn, tline->label_id, tline->obj.dim.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);
+ if (ret < 0)
+ return log_msg_ret("tit", ret);
+
+ ret = scene_obj_get_hw(scn, tline->label_id, NULL);
+ if (ret < 0)
+ return log_msg_ret("hei", ret);
+
+ y += ret * 2;
+ }
+
+ point = scn->highlight_id == tline->obj.id;
+ point &= !open;
+ scene_obj_flag_clrset(scn, tline->edit_id, SCENEOF_POINT,
+ point ? SCENEOF_POINT : 0);
+
+ return 0;
+}
+
+int scene_textline_send_key(struct scene *scn, struct scene_obj_textline *tline,
+ int key, struct expo_action *event)
+{
+ const bool open = tline->obj.flags & SCENEOF_OPEN;
+
+ log_debug("key=%d\n", key);
+ switch (key) {
+ case BKEY_QUIT:
+ if (open) {
+ event->type = EXPOACT_CLOSE;
+ event->select.id = tline->obj.id;
+
+ /* Copy the backup text from the scene buffer */
+ memcpy(abuf_data(&tline->buf), abuf_data(&scn->buf),
+ abuf_size(&scn->buf));
+ } else {
+ event->type = EXPOACT_QUIT;
+ log_debug("menu quit\n");
+ }
+ break;
+ case BKEY_SELECT:
+ if (!open)
+ break;
+ event->type = EXPOACT_CLOSE;
+ event->select.id = tline->obj.id;
+ key = '\n';
+ fallthrough;
+ default: {
+ struct udevice *cons = scn->expo->cons;
+ int ret;
+
+ ret = vidconsole_entry_restore(cons, &scn->entry_save);
+ if (ret)
+ return log_msg_ret("sav", ret);
+ ret = cread_line_process_ch(&scn->cls, key);
+ ret = vidconsole_entry_save(cons, &scn->entry_save);
+ if (ret)
+ return log_msg_ret("sav", ret);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int scene_textline_render_deps(struct scene *scn,
+ struct scene_obj_textline *tline)
+{
+ const bool open = tline->obj.flags & SCENEOF_OPEN;
+ struct udevice *cons = scn->expo->cons;
+ struct scene_obj_txt *txt;
+ int ret;
+
+ scene_render_deps(scn, tline->label_id);
+ scene_render_deps(scn, tline->edit_id);
+
+ /* show the vidconsole cursor if open */
+ if (open) {
+ /* get the position within the field */
+ txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
+ if (!txt)
+ return log_msg_ret("cur", -ENOENT);
+
+ if (txt->font_name || txt->font_size) {
+ ret = vidconsole_select_font(cons,
+ txt->font_name,
+ txt->font_size);
+ } else {
+ ret = vidconsole_select_font(cons, NULL, 0);
+ }
+
+ ret = vidconsole_entry_restore(cons, &scn->entry_save);
+ 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);
+ }
+
+ return 0;
+}
+
+int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline)
+{
+ struct udevice *cons = scn->expo->cons;
+ struct scene_obj_txt *txt;
+ int ret;
+
+ /* Copy the text into the scene buffer in case the edit is cancelled */
+ memcpy(abuf_data(&scn->buf), abuf_data(&tline->buf),
+ abuf_size(&scn->buf));
+
+ /* get the position of the editable */
+ txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
+ if (!txt)
+ return log_msg_ret("cur", -ENOENT);
+
+ vidconsole_set_cursor_pos(cons, txt->obj.dim.x, txt->obj.dim.y);
+ vidconsole_entry_start(cons);
+ cli_cread_init(&scn->cls, abuf_data(&tline->buf), tline->max_chars);
+ scn->cls.insert = true;
+ ret = vidconsole_entry_save(cons, &scn->entry_save);
+ if (ret)
+ return log_msg_ret("sav", ret);
+
+ return 0;
+}
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 6470b13..5bc0a92 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -176,6 +176,13 @@
help
Command to read the metadata and dump it's contents
+config CMD_HISTORY
+ bool "history"
+ depends on CMDLINE_EDITING
+ help
+ Show the command-line history, i.e. a list of commands that are in
+ the history buffer.
+
config CMD_LICENSE
bool "license"
select BUILD_BIN2C
diff --git a/cmd/Makefile b/cmd/Makefile
index 9bebf32..971f78a 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -91,6 +91,7 @@
obj-$(CONFIG_CMD_FWU_METADATA) += fwu_mdata.o
obj-$(CONFIG_CMD_GETTIME) += gettime.o
obj-$(CONFIG_CMD_GPIO) += gpio.o
+obj-$(CONFIG_CMD_HISTORY) += history.o
obj-$(CONFIG_CMD_HVC) += smccc.o
obj-$(CONFIG_CMD_I2C) += i2c.o
obj-$(CONFIG_CMD_IOTRACE) += iotrace.o
diff --git a/cmd/history.c b/cmd/history.c
new file mode 100644
index 0000000..b6bf467
--- /dev/null
+++ b/cmd/history.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <cli.h>
+
+static int do_history(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ cread_print_hist_list();
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ history, CONFIG_SYS_MAXARGS, 1, do_history,
+ "print command history",
+ ""
+);
diff --git a/common/cli_readline.c b/common/cli_readline.c
index e83743e..06b8d46 100644
--- a/common/cli_readline.c
+++ b/common/cli_readline.c
@@ -89,6 +89,14 @@
#define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
+static void getcmd_putchars(int count, int ch)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ getcmd_putch(ch);
+}
+
static void hist_init(void)
{
int i;
@@ -160,11 +168,10 @@
return ret;
}
-#ifndef CONFIG_CMDLINE_EDITING
-static void cread_print_hist_list(void)
+void cread_print_hist_list(void)
{
int i;
- unsigned long n;
+ uint n;
n = hist_num - hist_max;
@@ -179,36 +186,35 @@
i++;
}
}
-#endif /* CONFIG_CMDLINE_EDITING */
#define BEGINNING_OF_LINE() { \
- while (num) { \
+ while (cls->num) { \
getcmd_putch(CTL_BACKSPACE); \
- num--; \
+ cls->num--; \
} \
}
#define ERASE_TO_EOL() { \
- if (num < eol_num) { \
- printf("%*s", (int)(eol_num - num), ""); \
+ if (cls->num < cls->eol_num) { \
+ printf("%*s", (int)(cls->eol_num - cls->num), ""); \
do { \
getcmd_putch(CTL_BACKSPACE); \
- } while (--eol_num > num); \
+ } while (--cls->eol_num > cls->num); \
} \
}
-#define REFRESH_TO_EOL() { \
- if (num < eol_num) { \
- wlen = eol_num - num; \
- putnstr(buf + num, wlen); \
- num = eol_num; \
- } \
+#define REFRESH_TO_EOL() { \
+ if (cls->num < cls->eol_num) { \
+ uint wlen = cls->eol_num - cls->num; \
+ putnstr(buf + cls->num, wlen); \
+ cls->num = cls->eol_num; \
+ } \
}
-static void cread_add_char(char ichar, int insert, unsigned long *num,
- unsigned long *eol_num, char *buf, unsigned long len)
+static void cread_add_char(char ichar, int insert, uint *num,
+ uint *eol_num, char *buf, uint len)
{
- unsigned long wlen;
+ uint wlen;
/* room ??? */
if (insert || *num == *eol_num) {
@@ -239,8 +245,7 @@
}
static void cread_add_str(char *str, int strsize, int insert,
- unsigned long *num, unsigned long *eol_num,
- char *buf, unsigned long len)
+ uint *num, uint *eol_num, char *buf, uint len)
{
while (strsize--) {
cread_add_char(*str, insert, num, eol_num, buf, len);
@@ -248,121 +253,115 @@
}
}
-static int cread_line(const char *const prompt, char *buf, unsigned int *len,
- int timeout)
+int cread_line_process_ch(struct cli_line_state *cls, char ichar)
{
- struct cli_ch_state s_cch, *cch = &s_cch;
- unsigned long num = 0;
- unsigned long eol_num = 0;
- unsigned long wlen;
- char ichar;
- int insert = 1;
- int init_len = strlen(buf);
- int first = 1;
-
- cli_ch_init(cch);
+ char *buf = cls->buf;
- if (init_len)
- cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
+ /* ichar=0x0 when error occurs in U-Boot getc */
+ if (!ichar)
+ return -EAGAIN;
- while (1) {
- /* Check for saved characters */
- ichar = cli_ch_process(cch, 0);
+ if (ichar == '\n') {
+ putc('\n');
+ buf[cls->eol_num] = '\0'; /* terminate the string */
+ return 0;
+ }
- if (!ichar) {
- if (bootretry_tstc_timeout())
- return -2; /* timed out */
- if (first && timeout) {
- u64 etime = endtick(timeout);
+ switch (ichar) {
+ case CTL_CH('a'):
+ BEGINNING_OF_LINE();
+ break;
+ case CTL_CH('c'): /* ^C - break */
+ *buf = '\0'; /* discard input */
+ return -EINTR;
+ case CTL_CH('f'):
+ if (cls->num < cls->eol_num) {
+ getcmd_putch(buf[cls->num]);
+ cls->num++;
+ }
+ break;
+ case CTL_CH('b'):
+ if (cls->num) {
+ getcmd_putch(CTL_BACKSPACE);
+ cls->num--;
+ }
+ break;
+ case CTL_CH('d'):
+ if (cls->num < cls->eol_num) {
+ uint wlen;
- while (!tstc()) { /* while no incoming data */
- if (get_ticks() >= etime)
- return -2; /* timed out */
- schedule();
- }
- first = 0;
+ wlen = cls->eol_num - cls->num - 1;
+ if (wlen) {
+ memmove(&buf[cls->num], &buf[cls->num + 1],
+ wlen);
+ putnstr(buf + cls->num, wlen);
}
- ichar = getcmd_getch();
- ichar = cli_ch_process(cch, ichar);
+ getcmd_putch(' ');
+ do {
+ getcmd_putch(CTL_BACKSPACE);
+ } while (wlen--);
+ cls->eol_num--;
}
+ break;
+ case CTL_CH('k'):
+ ERASE_TO_EOL();
+ break;
+ case CTL_CH('e'):
+ REFRESH_TO_EOL();
+ break;
+ case CTL_CH('o'):
+ cls->insert = !cls->insert;
+ break;
+ case CTL_CH('w'):
+ if (cls->num) {
+ uint base, wlen;
- /* ichar=0x0 when error occurs in U-Boot getc */
- if (!ichar)
- continue;
+ for (base = cls->num - 1;
+ base >= 0 && buf[base] == ' ';)
+ base--;
+ for (; base > 0 && buf[base - 1] != ' ';)
+ base--;
- if (ichar == '\n') {
- putc('\n');
- break;
+ /* now delete chars from base to cls->num */
+ wlen = cls->num - base;
+ cls->eol_num -= wlen;
+ memmove(&buf[base], &buf[cls->num],
+ cls->eol_num - base + 1);
+ cls->num = base;
+ getcmd_putchars(wlen, CTL_BACKSPACE);
+ puts(buf + base);
+ getcmd_putchars(wlen, ' ');
+ getcmd_putchars(wlen + cls->eol_num - cls->num,
+ CTL_BACKSPACE);
}
-
- switch (ichar) {
- case CTL_CH('a'):
- BEGINNING_OF_LINE();
- break;
- case CTL_CH('c'): /* ^C - break */
- *buf = '\0'; /* discard input */
- return -1;
- case CTL_CH('f'):
- if (num < eol_num) {
- getcmd_putch(buf[num]);
- num++;
- }
- break;
- case CTL_CH('b'):
- if (num) {
- getcmd_putch(CTL_BACKSPACE);
- num--;
- }
- break;
- case CTL_CH('d'):
- if (num < eol_num) {
- wlen = eol_num - num - 1;
- if (wlen) {
- memmove(&buf[num], &buf[num+1], wlen);
- putnstr(buf + num, wlen);
- }
+ break;
+ case CTL_CH('x'):
+ case CTL_CH('u'):
+ BEGINNING_OF_LINE();
+ ERASE_TO_EOL();
+ break;
+ case DEL:
+ case DEL7:
+ case 8:
+ if (cls->num) {
+ uint wlen;
- getcmd_putch(' ');
- do {
- getcmd_putch(CTL_BACKSPACE);
- } while (wlen--);
- eol_num--;
- }
- break;
- case CTL_CH('k'):
- ERASE_TO_EOL();
- break;
- case CTL_CH('e'):
- REFRESH_TO_EOL();
- break;
- case CTL_CH('o'):
- insert = !insert;
- break;
- case CTL_CH('x'):
- case CTL_CH('u'):
- BEGINNING_OF_LINE();
- ERASE_TO_EOL();
- break;
- case DEL:
- case DEL7:
- case 8:
- if (num) {
- wlen = eol_num - num;
- num--;
- memmove(&buf[num], &buf[num+1], wlen);
+ wlen = cls->eol_num - cls->num;
+ cls->num--;
+ memmove(&buf[cls->num], &buf[cls->num + 1], wlen);
+ getcmd_putch(CTL_BACKSPACE);
+ putnstr(buf + cls->num, wlen);
+ getcmd_putch(' ');
+ do {
getcmd_putch(CTL_BACKSPACE);
- putnstr(buf + num, wlen);
- getcmd_putch(' ');
- do {
- getcmd_putch(CTL_BACKSPACE);
- } while (wlen--);
- eol_num--;
- }
- break;
- case CTL_CH('p'):
- case CTL_CH('n'):
- {
+ } while (wlen--);
+ cls->eol_num--;
+ }
+ break;
+ case CTL_CH('p'):
+ case CTL_CH('n'):
+ if (cls->history) {
char *hline;
if (ichar == CTL_CH('p'))
@@ -372,7 +371,7 @@
if (!hline) {
getcmd_cbeep();
- continue;
+ break;
}
/* nuke the current line */
@@ -384,44 +383,123 @@
/* copy new line into place and display */
strcpy(buf, hline);
- eol_num = strlen(buf);
+ cls->eol_num = strlen(buf);
REFRESH_TO_EOL();
- continue;
+ break;
}
-#ifdef CONFIG_AUTO_COMPLETE
- case '\t': {
+ break;
+ case '\t':
+ if (IS_ENABLED(CONFIG_AUTO_COMPLETE) && cls->cmd_complete) {
int num2, col;
/* do not autocomplete when in the middle */
- if (num < eol_num) {
+ if (cls->num < cls->eol_num) {
getcmd_cbeep();
break;
}
- buf[num] = '\0';
- col = strlen(prompt) + eol_num;
- num2 = num;
- if (cmd_auto_complete(prompt, buf, &num2, &col)) {
- col = num2 - num;
- num += col;
- eol_num += col;
+ buf[cls->num] = '\0';
+ col = strlen(cls->prompt) + cls->eol_num;
+ num2 = cls->num;
+ if (cmd_auto_complete(cls->prompt, buf, &num2, &col)) {
+ col = num2 - cls->num;
+ cls->num += col;
+ cls->eol_num += col;
}
break;
}
-#endif
- default:
- cread_add_char(ichar, insert, &num, &eol_num, buf,
- *len);
- break;
+ fallthrough;
+ default:
+ cread_add_char(ichar, cls->insert, &cls->num, &cls->eol_num,
+ buf, cls->len);
+ break;
+ }
+
+ /*
+ * keep the string terminated...if we added a char at the end then we
+ * want a \0 after it
+ */
+ buf[cls->eol_num] = '\0';
+
+ return -EAGAIN;
+}
+
+void cli_cread_init(struct cli_line_state *cls, char *buf, uint buf_size)
+{
+ int init_len = strlen(buf);
+
+ memset(cls, '\0', sizeof(struct cli_line_state));
+ cls->insert = true;
+ cls->buf = buf;
+ cls->len = buf_size;
+
+ if (init_len)
+ cread_add_str(buf, init_len, 0, &cls->num, &cls->eol_num, buf,
+ buf_size);
+}
+
+static int cread_line(const char *const prompt, char *buf, unsigned int *len,
+ int timeout)
+{
+ struct cli_ch_state s_cch, *cch = &s_cch;
+ struct cli_line_state s_cls, *cls = &s_cls;
+ char ichar;
+ int first = 1;
+
+ cli_ch_init(cch);
+ cli_cread_init(cls, buf, *len);
+ cls->prompt = prompt;
+ cls->history = true;
+ cls->cmd_complete = true;
+
+ while (1) {
+ int ret;
+
+ /* Check for saved characters */
+ ichar = cli_ch_process(cch, 0);
+
+ if (!ichar) {
+ if (bootretry_tstc_timeout())
+ return -2; /* timed out */
+ if (first && timeout) {
+ u64 etime = endtick(timeout);
+
+ while (!tstc()) { /* while no incoming data */
+ if (get_ticks() >= etime)
+ return -2; /* timed out */
+ schedule();
+ }
+ first = 0;
+ }
+
+ ichar = getcmd_getch();
+ ichar = cli_ch_process(cch, ichar);
}
+
+ ret = cread_line_process_ch(cls, ichar);
+ if (ret == -EINTR)
+ return -1;
+ else if (!ret)
+ break;
}
- *len = eol_num;
- buf[eol_num] = '\0'; /* lose the newline */
+ *len = cls->eol_num;
if (buf[0] && buf[0] != CREAD_HIST_CHAR)
cread_add_to_hist(buf);
hist_cur = hist_add_idx;
+ return 0;
+}
+
+#else /* !CONFIG_CMDLINE_EDITING */
+
+static inline void hist_init(void)
+{
+}
+
+static int cread_line(const char *const prompt, char *buf, unsigned int *len,
+ int timeout)
+{
return 0;
}
@@ -440,41 +518,22 @@
return cli_readline_into_buffer(prompt, console_buffer, 0);
}
-
-int cli_readline_into_buffer(const char *const prompt, char *buffer,
- int timeout)
+/**
+ * cread_line_simple() - Simple (small) command-line reader
+ *
+ * This supports only basic editing, with no cursor movement
+ *
+ * @prompt: Prompt to display
+ * @p: Text buffer to edit
+ * Return: length of text buffer, or -1 if input was cannncelled (Ctrl-C)
+ */
+static int cread_line_simple(const char *const prompt, char *p)
{
- char *p = buffer;
-#ifdef CONFIG_CMDLINE_EDITING
- unsigned int len = CONFIG_SYS_CBSIZE;
- int rc;
- static int initted;
-
- /*
- * History uses a global array which is not
- * writable until after relocation to RAM.
- * Revert to non-history version if still
- * running from flash.
- */
- if (gd->flags & GD_FLG_RELOC) {
- if (!initted) {
- hist_init();
- initted = 1;
- }
-
- if (prompt)
- puts(prompt);
-
- rc = cread_line(prompt, p, &len, timeout);
- return rc < 0 ? rc : len;
-
- } else {
-#endif /* CONFIG_CMDLINE_EDITING */
char *p_buf = p;
- int n = 0; /* buffer index */
- int plen = 0; /* prompt length */
- int col; /* output column cnt */
- char c;
+ int n = 0; /* buffer index */
+ int plen = 0; /* prompt length */
+ int col; /* output column cnt */
+ char c;
/* print prompt */
if (prompt) {
@@ -528,14 +587,15 @@
continue;
default:
- /*
- * Must be a normal character then
- */
- if (n < CONFIG_SYS_CBSIZE-2) {
- if (c == '\t') { /* expand TABs */
-#ifdef CONFIG_AUTO_COMPLETE
+ /* Must be a normal character then */
+ if (n >= CONFIG_SYS_CBSIZE - 2) { /* Buffer full */
+ putc('\a');
+ break;
+ }
+ if (c == '\t') { /* expand TABs */
+ if (IS_ENABLED(CONFIG_AUTO_COMPLETE)) {
/*
- * if auto completion triggered just
+ * if auto-completion triggered just
* continue
*/
*p = '\0';
@@ -545,29 +605,55 @@
p = p_buf + n; /* reset */
continue;
}
-#endif
- puts(tab_seq + (col & 07));
- col += 8 - (col & 07);
- } else {
- char __maybe_unused buf[2];
-
- /*
- * Echo input using puts() to force an
- * LCD flush if we are using an LCD
- */
- ++col;
- buf[0] = c;
- buf[1] = '\0';
- puts(buf);
}
- *p++ = c;
- ++n;
- } else { /* Buffer full */
- putc('\a');
+ puts(tab_seq + (col & 07));
+ col += 8 - (col & 07);
+ } else {
+ char __maybe_unused buf[2];
+
+ /*
+ * Echo input using puts() to force an LCD
+ * flush if we are using an LCD
+ */
+ ++col;
+ buf[0] = c;
+ buf[1] = '\0';
+ puts(buf);
}
+ *p++ = c;
+ ++n;
+ break;
}
}
-#ifdef CONFIG_CMDLINE_EDITING
+}
+
+int cli_readline_into_buffer(const char *const prompt, char *buffer,
+ int timeout)
+{
+ char *p = buffer;
+ uint len = CONFIG_SYS_CBSIZE;
+ int rc;
+ static int initted;
+
+ /*
+ * History uses a global array which is not
+ * writable until after relocation to RAM.
+ * Revert to non-history version if still
+ * running from flash.
+ */
+ if (IS_ENABLED(CONFIG_CMDLINE_EDITING) && (gd->flags & GD_FLG_RELOC)) {
+ if (!initted) {
+ hist_init();
+ initted = 1;
+ }
+
+ if (prompt)
+ puts(prompt);
+
+ rc = cread_line(prompt, p, &len, timeout);
+ return rc < 0 ? rc : len;
+
+ } else {
+ return cread_line_simple(prompt, p);
}
-#endif
}
diff --git a/doc/develop/cedit.rst b/doc/develop/cedit.rst
index 63dff9d..82305b9 100644
--- a/doc/develop/cedit.rst
+++ b/doc/develop/cedit.rst
@@ -162,7 +162,8 @@
- Writing an FDT file to a filesystem
- Writing to U-Boot's environment variables, which are then typically stored in
a persistent manner
-- Writing to CMOS RAM registers (common on x86 machines)
+- Writing to CMOS RAM registers (common on x86 machines). Note that textline
+ objects do not appear in CMOS RAM registers
For now, reading and writing settings is not automatic. See the
:doc:`../usage/cmd/cedit` for how to do this on the command line or in a
diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst
index f137619..c87b6ec 100644
--- a/doc/develop/expo.rst
+++ b/doc/develop/expo.rst
@@ -63,9 +63,12 @@
within the menu. Items can also have a preview image, which is shown when the
item is highlighted.
-All components have a name. This is purely for debugging, so it is easy to see
-what object is referred to. Of course the ID numbers can help as well, but they
-are less easy to distinguish.
+A `textline object` contains a label and an editable string.
+
+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
+distinguish.
While the expo implementation provides support for handling keypresses and
rendering on the display or serial port, it does not actually deal with reading
@@ -136,7 +139,9 @@
sequences into keys. However, expo has some special menu-key codes for
navigating the interface. These are defined in `enum bootmenu_key` and include
`BKEY_UP` for moving up and `BKEY_SELECT` for selecting an item. You can use
-`bootmenu_conv_key()` to convert an ASCII key into one of these.
+`bootmenu_conv_key()` to convert an ASCII key into one of these, but if it
+returns a value >= `BKEY_FIRST_EXTRA` then you should pass the unmodified ASCII
+key to the expo, since it may be used by textline objects.
Once a keypress is decoded, call `expo_send_key()` to send it to the expo. This
may cause an update to the expo state and may produce an action.
@@ -312,6 +317,9 @@
"menu"
Menu containing items which can be selected by the user
+ "textline"
+ A line of text which can be edited
+
id
type: u32, required
@@ -362,6 +370,26 @@
Specifies the description for each item in the menu. These are currently
only intended for use in simple mode.
+Textline nodes have the following additional properties:
+
+label / label-id
+ type: string / u32, required
+
+ Specifies the label of the textline. This is shown to the left of the area
+ for this textline.
+
+edit-id
+ type: u32, required
+
+ Specifies the ID of the of the editable text object. This can be used to
+ obtain the text from the textline
+
+max-chars:
+ type: u32, required
+
+ Specifies the maximum number of characters permitted to be in the textline.
+ The user will be prevented from adding more.
+
Expo layout
~~~~~~~~~~~
@@ -401,6 +429,9 @@
ID_AC_ON,
ID_AC_MEMORY,
+ ID_MACHINE_NAME,
+ ID_MACHINE_NAME_EDIT,
+
ID_DYNAMIC_START,
*/
@@ -447,6 +478,13 @@
item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
};
+
+ machine-name {
+ id = <ID_MACHINE_NAME>;
+ type = "textline";
+ max-chars = <20>;
+ title = "Machine name";
+ edit-id = <ID_MACHINE_NAME_EDIT>;
};
};
@@ -474,7 +512,7 @@
- Image formats other than BMP
- Use of ANSI sequences to control a serial terminal
- Colour selection
-- Support for more widgets, e.g. text, numeric, radio/option
+- Support for more widgets, e.g. numeric, radio/option
- Mouse support
- Integrate Nuklear, NxWidgets or some other library for a richer UI
- Optimise rendering by only updating the display with changes since last render
diff --git a/doc/usage/cmd/history.rst b/doc/usage/cmd/history.rst
new file mode 100644
index 0000000..33d3fcd
--- /dev/null
+++ b/doc/usage/cmd/history.rst
@@ -0,0 +1,67 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+history command
+===============
+
+Synopis
+-------
+
+::
+
+ history
+
+Description
+-----------
+
+The *history* command shows a list of previously entered commands on the
+command line. When U-Boot starts, this it is initially empty. Each new command
+entered is added to the list.
+
+Normally these commands can be accessed by pressing the `up arrow` and
+`down arrow` keys, which cycle through the list. The `history` command provides
+a simple way to view the list.
+
+Example
+-------
+
+This example shows entering three commands, then `history`. Note that `history`
+itself is added to the list.
+
+::
+
+ => bootflow scan -l
+ Scanning for bootflows in all bootdevs
+ Seq Method State Uclass Part Name Filename
+ --- ----------- ------ -------- ---- ------------------------ ----------------
+ Scanning global bootmeth 'firmware0':
+ Hunting with: simple_bus
+ Found 2 extension board(s).
+ Scanning bootdev 'mmc2.bootdev':
+ Scanning bootdev 'mmc1.bootdev':
+ 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf
+ No more bootdevs
+ --- ----------- ------ -------- ---- ------------------------ ----------------
+ (1 bootflow, 1 valid)
+ => bootflow select 0
+ => bootflow info
+ Name: mmc1.bootdev.part_1
+ Device: mmc1.bootdev
+ Block dev: mmc1.blk
+ Method: extlinux
+ State: ready
+ Partition: 1
+ Subdir: (none)
+ Filename: /extlinux/extlinux.conf
+ Buffer: aebdea0
+ Size: 253 (595 bytes)
+ OS: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+ Cmdline: (none)
+ Logo: (none)
+ FDT: <NULL>
+ Error: 0
+ => history
+ bootflow scan -l
+ bootflow select 0
+ bootflow info
+ history
+ =>
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index fa70292..98b4719 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -67,6 +67,7 @@
cmd/fwu_mdata
cmd/gpio
cmd/gpt
+ cmd/history
cmd/host
cmd/imxtract
cmd/load
diff --git a/drivers/video/console_core.c b/drivers/video/console_core.c
index b5d0e3d..d17764d 100644
--- a/drivers/video/console_core.c
+++ b/drivers/video/console_core.c
@@ -176,6 +176,37 @@
return ret;
}
+int draw_cursor_vertically(void **line, struct video_priv *vid_priv,
+ uint height, bool direction)
+{
+ int step, line_step, pbytes, ret;
+ uint value;
+ void *dst;
+
+ ret = check_bpix_support(vid_priv->bpix);
+ if (ret)
+ return ret;
+
+ pbytes = VNBYTES(vid_priv->bpix);
+ if (direction) {
+ step = -pbytes;
+ line_step = -vid_priv->line_length;
+ } else {
+ step = pbytes;
+ line_step = vid_priv->line_length;
+ }
+
+ value = vid_priv->colour_fg;
+
+ for (int row = 0; row < height; row++) {
+ dst = *line;
+ for (int col = 0; col < VIDCONSOLE_CURSOR_WIDTH; col++)
+ fill_pixel_and_goto_next(&dst, value, pbytes, step);
+ *line += line_step;
+ }
+ return ret;
+}
+
int console_probe(struct udevice *dev)
{
return console_set_font(dev, fonts);
diff --git a/drivers/video/console_normal.c b/drivers/video/console_normal.c
index 413c7ab..a023129 100644
--- a/drivers/video/console_normal.c
+++ b/drivers/video/console_normal.c
@@ -97,6 +97,34 @@
return VID_TO_POS(fontdata->width);
}
+static int console_set_cursor_visible(struct udevice *dev, bool visible,
+ uint x, uint y, uint index)
+{
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
+ struct udevice *vid = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+ struct console_simple_priv *priv = dev_get_priv(dev);
+ struct video_fontdata *fontdata = priv->fontdata;
+ int pbytes = VNBYTES(vid_priv->bpix);
+ void *start, *line;
+
+ /* for now, this is not used outside expo */
+ if (!IS_ENABLED(CONFIG_EXPO))
+ return -ENOSYS;
+
+ x += index * fontdata->width;
+ start = vid_priv->fb + y * vid_priv->line_length + x * pbytes;
+
+ /* place the cursor 1 pixel before the start of the next char */
+ x -= 1;
+
+ line = start;
+ draw_cursor_vertically(&line, vid_priv, vc_priv->y_charsize,
+ NORMAL_DIRECTION);
+
+ return 0;
+}
+
struct vidconsole_ops console_ops = {
.putc_xy = console_putc_xy,
.move_rows = console_move_rows,
@@ -104,6 +132,7 @@
.get_font_size = console_simple_get_font_size,
.get_font = console_simple_get_font,
.select_font = console_simple_select_font,
+ .set_cursor_visible = console_set_cursor_visible,
};
U_BOOT_DRIVER(vidconsole_normal) = {
diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c
index 0f9bb49..14fb81e 100644
--- a/drivers/video/console_truetype.c
+++ b/drivers/video/console_truetype.c
@@ -4,6 +4,7 @@
*/
#include <common.h>
+#include <abuf.h>
#include <dm.h>
#include <log.h>
#include <malloc.h>
@@ -175,6 +176,17 @@
int pos_ptr;
};
+/**
+ * struct console_tt_store - Format used for save/restore of entry information
+ *
+ * @priv: Private data
+ * @cur: Current cursor position
+ */
+struct console_tt_store {
+ struct console_tt_priv priv;
+ struct pos_info cur;
+};
+
static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
{
struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
@@ -706,8 +718,8 @@
return 0;
}
-int truetype_measure(struct udevice *dev, const char *name, uint size,
- const char *text, struct vidconsole_bbox *bbox)
+static int truetype_measure(struct udevice *dev, const char *name, uint size,
+ const char *text, struct vidconsole_bbox *bbox)
{
struct console_tt_metrics *met;
stbtt_fontinfo *font;
@@ -750,6 +762,177 @@
return 0;
}
+static int truetype_nominal(struct udevice *dev, const char *name, uint size,
+ uint num_chars, struct vidconsole_bbox *bbox)
+{
+ struct console_tt_metrics *met;
+ stbtt_fontinfo *font;
+ int lsb, advance;
+ int width;
+ int ret;
+
+ ret = get_metrics(dev, name, size, &met);
+ if (ret)
+ return log_msg_ret("sel", ret);
+
+ font = &met->font;
+ width = 0;
+
+ /* First get some basic metrics about this character */
+ stbtt_GetCodepointHMetrics(font, 'W', &advance, &lsb);
+
+ width = advance;
+
+ bbox->valid = true;
+ bbox->x0 = 0;
+ bbox->y0 = 0;
+ bbox->x1 = tt_ceil((double)width * num_chars * met->scale);
+ bbox->y1 = met->font_size;
+
+ return 0;
+}
+
+static int truetype_entry_save(struct udevice *dev, struct abuf *buf)
+{
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
+ struct console_tt_priv *priv = dev_get_priv(dev);
+ struct console_tt_store store;
+ const uint size = sizeof(store);
+
+ /*
+ * store the whole priv structure as it is simpler that picking out
+ * what we need
+ */
+ if (!abuf_realloc(buf, size))
+ return log_msg_ret("sav", -ENOMEM);
+
+ store.priv = *priv;
+ store.cur.xpos_frac = vc_priv->xcur_frac;
+ store.cur.ypos = vc_priv->ycur;
+ memcpy(abuf_data(buf), &store, size);
+
+ return 0;
+}
+
+static int truetype_entry_restore(struct udevice *dev, struct abuf *buf)
+{
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
+ struct console_tt_priv *priv = dev_get_priv(dev);
+ struct console_tt_store store;
+
+ memcpy(&store, abuf_data(buf), sizeof(store));
+
+ vc_priv->xcur_frac = store.cur.xpos_frac;
+ vc_priv->ycur = store.cur.ypos;
+ priv->pos_ptr = store.priv.pos_ptr;
+ memcpy(priv->pos, store.priv.pos,
+ store.priv.pos_ptr * sizeof(struct pos_info));
+
+ return 0;
+}
+
+static int truetype_set_cursor_visible(struct udevice *dev, bool visible,
+ uint x, uint y, uint index)
+{
+ struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
+ struct udevice *vid = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+ struct console_tt_priv *priv = dev_get_priv(dev);
+ struct console_tt_metrics *met = priv->cur_met;
+ uint row, width, height, xoff;
+ void *start, *line;
+ uint out, val;
+ int ret;
+
+ if (!visible)
+ return 0;
+
+ /*
+ * figure out where to place the cursor. This driver ignores the
+ * passed-in values, since an entry_restore() must have been done before
+ * calling this function.
+ */
+ if (index < priv->pos_ptr)
+ x = VID_TO_PIXEL(priv->pos[index].xpos_frac);
+ else
+ x = VID_TO_PIXEL(vc_priv->xcur_frac);
+
+ y = vc_priv->ycur;
+ height = met->font_size;
+ xoff = 0;
+
+ val = vid_priv->colour_bg ? 0 : 255;
+ width = VIDCONSOLE_CURSOR_WIDTH;
+
+ /* Figure out where to write the cursor in the frame buffer */
+ start = vid_priv->fb + y * vid_priv->line_length +
+ x * VNBYTES(vid_priv->bpix);
+ line = start;
+
+ /* draw a vertical bar in the correct position */
+ for (row = 0; row < height; row++) {
+ switch (vid_priv->bpix) {
+ case VIDEO_BPP8:
+ if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
+ u8 *dst = line + xoff;
+ int i;
+
+ out = val;
+ for (i = 0; i < width; i++) {
+ if (vid_priv->colour_fg)
+ *dst++ |= out;
+ else
+ *dst++ &= out;
+ }
+ }
+ break;
+ case VIDEO_BPP16: {
+ u16 *dst = (u16 *)line + xoff;
+ int i;
+
+ if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
+ for (i = 0; i < width; i++) {
+ out = val >> 3 |
+ (val >> 2) << 5 |
+ (val >> 3) << 11;
+ if (vid_priv->colour_fg)
+ *dst++ |= out;
+ else
+ *dst++ &= out;
+ }
+ }
+ break;
+ }
+ case VIDEO_BPP32: {
+ u32 *dst = (u32 *)line + xoff;
+ int i;
+
+ if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
+ for (i = 0; i < width; i++) {
+ int out;
+
+ out = val | val << 8 | val << 16;
+ if (vid_priv->colour_fg)
+ *dst++ |= out;
+ else
+ *dst++ &= out;
+ }
+ }
+ break;
+ }
+ default:
+ return -ENOSYS;
+ }
+
+ line += vid_priv->line_length;
+ }
+ ret = vidconsole_sync_copy(dev, start, line);
+ if (ret)
+ return ret;
+
+ return video_sync(vid, true);
+}
+
const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep)
{
struct console_tt_priv *priv = dev_get_priv(dev);
@@ -802,6 +985,10 @@
.get_font_size = console_truetype_get_font_size,
.select_font = truetype_select_font,
.measure = truetype_measure,
+ .nominal = truetype_nominal,
+ .entry_save = truetype_entry_save,
+ .entry_restore = truetype_entry_restore,
+ .set_cursor_visible = truetype_set_cursor_visible
};
U_BOOT_DRIVER(vidconsole_truetype) = {
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index b5b3b66..22d55df 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -10,6 +10,7 @@
#define LOG_CATEGORY UCLASS_VIDEO_CONSOLE
#include <common.h>
+#include <abuf.h>
#include <command.h>
#include <console.h>
#include <log.h>
@@ -47,7 +48,7 @@
return ops->set_row(dev, row, clr);
}
-static int vidconsole_entry_start(struct udevice *dev)
+int vidconsole_entry_start(struct udevice *dev)
{
struct vidconsole_ops *ops = vidconsole_get_ops(dev);
@@ -618,6 +619,74 @@
return 0;
}
+int vidconsole_nominal(struct udevice *dev, const char *name, uint size,
+ uint num_chars, struct vidconsole_bbox *bbox)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+ struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+ int ret;
+
+ if (ops->measure) {
+ ret = ops->nominal(dev, name, size, num_chars, bbox);
+ if (ret != -ENOSYS)
+ return ret;
+ }
+
+ bbox->valid = true;
+ bbox->x0 = 0;
+ bbox->y0 = 0;
+ bbox->x1 = priv->x_charsize * num_chars;
+ bbox->y1 = priv->y_charsize;
+
+ return 0;
+}
+
+int vidconsole_entry_save(struct udevice *dev, struct abuf *buf)
+{
+ struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+ int ret;
+
+ if (ops->measure) {
+ ret = ops->entry_save(dev, buf);
+ if (ret != -ENOSYS)
+ return ret;
+ }
+
+ /* no data so make sure the buffer is empty */
+ abuf_realloc(buf, 0);
+
+ return 0;
+}
+
+int vidconsole_entry_restore(struct udevice *dev, struct abuf *buf)
+{
+ struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+ int ret;
+
+ if (ops->measure) {
+ ret = ops->entry_restore(dev, buf);
+ if (ret != -ENOSYS)
+ return ret;
+ }
+
+ return 0;
+}
+
+int vidconsole_set_cursor_visible(struct udevice *dev, bool visible,
+ uint x, uint y, uint index)
+{
+ struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+ int ret;
+
+ if (ops->set_cursor_visible) {
+ ret = ops->set_cursor_visible(dev, visible, x, y, index);
+ if (ret != -ENOSYS)
+ return ret;
+ }
+
+ return 0;
+}
+
void vidconsole_push_colour(struct udevice *dev, enum colour_idx fg,
enum colour_idx bg, struct vidconsole_colour *old)
{
diff --git a/drivers/video/vidconsole_internal.h b/drivers/video/vidconsole_internal.h
index c41edd4..0ec581b 100644
--- a/drivers/video/vidconsole_internal.h
+++ b/drivers/video/vidconsole_internal.h
@@ -93,6 +93,30 @@
struct video_fontdata *fontdata, bool direction);
/**
+ * draw_cursor_vertically() - Draw a simple vertical cursor
+ *
+ * @line: pointer to framebuffer buffer: upper left cursor corner
+ * @vid_priv: driver private data
+ * @height: height of the cursor in pixels
+ * @param direction controls cursor orientation. Can be normal or flipped.
+ * When normal: When flipped:
+ *|-----------------------------------------------|
+ *| * | line stepping |
+ *| ^ * * * * * | | |
+ *| | * * | v * * |
+ *| | | * * * * * |
+ *| line stepping | * |
+ *| | |
+ *| stepping -> | <<- stepping |
+ *|---!!we're starting from upper left char corner|
+ *|-----------------------------------------------|
+ *
+ * Return: 0, if success, or else error code.
+ */
+int draw_cursor_vertically(void **line, struct video_priv *vid_priv,
+ uint height, bool direction);
+
+/**
* console probe function.
*
* @param dev a pointer to device.
diff --git a/include/cli.h b/include/cli.h
index 094a660..e183d56 100644
--- a/include/cli.h
+++ b/include/cli.h
@@ -8,6 +8,7 @@
#define __CLI_H
#include <stdbool.h>
+#include <linux/types.h>
/**
* struct cli_ch_state - state information for reading cmdline characters
@@ -25,6 +26,29 @@
};
/**
+ * struct cli_line_state - state of the line editor
+ *
+ * @num: Current cursor position, where 0 is the start
+ * @eol_num: Number of characters in the buffer
+ * @insert: true if in 'insert' mode
+ * @history: true if history should be accessible
+ * @cmd_complete: true if tab completion should be enabled (requires @prompt to
+ * be set)
+ * @buf: Buffer containing line
+ * @prompt: Prompt for the line
+ */
+struct cli_line_state {
+ uint num;
+ uint eol_num;
+ uint len;
+ bool insert;
+ bool history;
+ bool cmd_complete;
+ char *buf;
+ const char *prompt;
+};
+
+/**
* Go into the command loop
*
* This will return if we get a timeout waiting for a command. See
@@ -229,4 +253,31 @@
*/
int cli_ch_process(struct cli_ch_state *cch, int ichar);
+/**
+ * cread_line_process_ch() - Process a character for line input
+ *
+ * @cls: CLI line state
+ * @ichar: Character to process
+ * Return: 0 if input is complete, with line in cls->buf, -EINTR if input was
+ * cancelled with Ctrl-C, -EAGAIN if more characters are needed
+ */
+int cread_line_process_ch(struct cli_line_state *cls, char ichar);
+
+/**
+ * cli_cread_init() - Set up a new cread struct
+ *
+ * Sets up a new cread state, with history and cmd_complete set to false
+ *
+ * After calling this, you can use cread_line_process_ch() to process characters
+ * received from the user.
+ *
+ * @cls: CLI line state
+ * @buf: Text buffer containing the initial text
+ * @buf_size: Buffer size, including nul terminator
+ */
+void cli_cread_init(struct cli_line_state *cls, char *buf, uint buf_size);
+
+/** cread_print_hist_list() - Print the command-line history list */
+void cread_print_hist_list(void);
+
#endif
diff --git a/include/command.h b/include/command.h
index 34ea989..1c4ec42 100644
--- a/include/command.h
+++ b/include/command.h
@@ -95,6 +95,12 @@
char *cmdv[]);
int cmd_auto_complete(const char *const prompt, char *buf, int *np,
int *colp);
+#else
+static inline int cmd_auto_complete(const char *const prompt, char *buf,
+ int *np, int *colp)
+{
+ return 0;
+}
#endif
/**
diff --git a/include/expo.h b/include/expo.h
index 9d2e817..264745f 100644
--- a/include/expo.h
+++ b/include/expo.h
@@ -7,11 +7,14 @@
#ifndef __EXPO_H
#define __EXPO_H
+#include <abuf.h>
#include <dm/ofnode_decl.h>
#include <linux/list.h>
struct udevice;
+#include <cli.h>
+
/**
* enum expoact_type - types of actions reported by the expo
*
@@ -121,6 +124,9 @@
* @id: ID number of the scene
* @title_id: String ID of title of the scene (allocated)
* @highlight_id: ID of highlighted object, if any
+ * @cls: cread state to use for input
+ * @buf: Buffer for input
+ * @entry_save: Buffer to hold vidconsole text-entry information
* @sibling: Node to link this scene to its siblings
* @obj_head: List of objects in the scene
*/
@@ -130,6 +136,9 @@
uint id;
uint title_id;
uint highlight_id;
+ struct cli_line_state cls;
+ struct abuf buf;
+ struct abuf entry_save;
struct list_head sibling;
struct list_head obj_head;
};
@@ -141,12 +150,16 @@
* @SCENEOBJT_IMAGE: Image data to render
* @SCENEOBJT_TEXT: Text line to render
* @SCENEOBJT_MENU: Menu containing items the user can select
+ * @SCENEOBJT_TEXTLINE: Line of text the user can edit
*/
enum scene_obj_t {
SCENEOBJT_NONE = 0,
SCENEOBJT_IMAGE,
SCENEOBJT_TEXT,
+
+ /* types from here on can be highlighted */
SCENEOBJT_MENU,
+ SCENEOBJT_TEXTLINE,
};
/**
@@ -178,6 +191,11 @@
SCENEOF_OPEN = 1 << 2,
};
+enum {
+ /* Maximum number of characters allowed in an line editor */
+ EXPO_MAX_CHARS = 250,
+};
+
/**
* struct scene_obj - information about an object in a scene
*
@@ -203,6 +221,12 @@
struct list_head sibling;
};
+/* object can be highlighted when moving around expo */
+static inline bool scene_obj_can_highlight(const struct scene_obj *obj)
+{
+ return obj->type >= SCENEOBJT_MENU;
+}
+
/**
* struct scene_obj_img - information about an image object in a scene
*
@@ -297,6 +321,27 @@
};
/**
+ * struct scene_obj_textline - information about a textline in a scene
+ *
+ * A textline has a prompt and a line of editable text
+ *
+ * @obj: Basic object information
+ * @label_id: ID of the label text, or 0 if none
+ * @edit_id: ID of the editable text
+ * @max_chars: Maximum number of characters allowed
+ * @buf: Text buffer containing current text
+ * @pos: Cursor position
+ */
+struct scene_obj_textline {
+ struct scene_obj obj;
+ uint label_id;
+ uint edit_id;
+ uint max_chars;
+ struct abuf buf;
+ uint pos;
+};
+
+/**
* expo_new() - create a new expo
*
* Allocates a new expo
@@ -505,7 +550,7 @@
struct scene_obj_txt **txtp);
/**
- * scene_txt_str() - add a new string to expr and text object to a scene
+ * scene_txt_str() - add a new string to expo and text object to a scene
*
* @scn: Scene to update
* @name: Name to use (this is allocated by this call)
@@ -531,6 +576,19 @@
struct scene_obj_menu **menup);
/**
+ * scene_textline() - create a textline
+ *
+ * @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)
+ * @max_chars: Maximum length of the textline in characters
+ * @tlinep: If non-NULL, returns the new object
+ * Returns: ID number for the object (typically @id), or -ve on error
+ */
+int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars,
+ struct scene_obj_textline **tlinep);
+
+/**
* scene_txt_set_font() - Set the font for an object
*
* @scn: Scene to update
diff --git a/include/menu.h b/include/menu.h
index 64ce89b..6571c39 100644
--- a/include/menu.h
+++ b/include/menu.h
@@ -50,12 +50,17 @@
BKEY_DOWN,
BKEY_SELECT,
BKEY_QUIT,
+ BKEY_SAVE,
+
+ /* 'extra' keys, which are used by menus but not cedit */
BKEY_PLUS,
BKEY_MINUS,
BKEY_SPACE,
- BKEY_SAVE,
BKEY_COUNT,
+
+ /* Keys from here on are not used by cedit */
+ BKEY_FIRST_EXTRA = BKEY_PLUS,
};
/**
diff --git a/include/test/cedit-test.h b/include/test/cedit-test.h
index 349df75..475ecc9 100644
--- a/include/test/cedit-test.h
+++ b/include/test/cedit-test.h
@@ -24,6 +24,9 @@
#define ID_AC_ON 11
#define ID_AC_MEMORY 12
-#define ID_DYNAMIC_START 13
+#define ID_MACHINE_NAME 13
+#define ID_MACHINE_NAME_EDIT 14
+
+#define ID_DYNAMIC_START 15
#endif
diff --git a/include/video_console.h b/include/video_console.h
index 2694e44..bde67fa 100644
--- a/include/video_console.h
+++ b/include/video_console.h
@@ -8,6 +8,7 @@
#include <video.h>
+struct abuf;
struct video_priv;
#define VID_FRAC_DIV 256
@@ -15,6 +16,11 @@
#define VID_TO_PIXEL(x) ((x) / VID_FRAC_DIV)
#define VID_TO_POS(x) ((x) * VID_FRAC_DIV)
+enum {
+ /* cursor width in pixels */
+ VIDCONSOLE_CURSOR_WIDTH = 2,
+};
+
/**
* struct vidconsole_priv - uclass-private data about a console device
*
@@ -224,6 +230,60 @@
*/
int (*measure)(struct udevice *dev, const char *name, uint size,
const char *text, struct vidconsole_bbox *bbox);
+
+ /**
+ * nominal() - Measure the expected width of a line of text
+ *
+ * Uses an average font width and nominal height
+ *
+ * @dev: Console device to use
+ * @name: Font name, NULL for default
+ * @size: Font size, ignored if @name is NULL
+ * @num_chars: Number of characters to use
+ * @bbox: Returns nounding box of @num_chars characters
+ * Returns: 0 if OK, -ve on error
+ */
+ int (*nominal)(struct udevice *dev, const char *name, uint size,
+ uint num_chars, struct vidconsole_bbox *bbox);
+
+ /**
+ * entry_save() - Save any text-entry information for later use
+ *
+ * Saves text-entry context such as a list of positions for each
+ * character in the string.
+ *
+ * @dev: Console device to use
+ * @buf: Buffer to hold saved data
+ * Return: 0 if OK, -ENOMEM if out of memory
+ */
+ int (*entry_save)(struct udevice *dev, struct abuf *buf);
+
+ /**
+ * entry_restore() - Restore text-entry information for current use
+ *
+ * Restores text-entry context such as a list of positions for each
+ * character in the string.
+ *
+ * @dev: Console device to use
+ * @buf: Buffer containing data to restore
+ * Return: 0 if OK, -ve on error
+ */
+ int (*entry_restore)(struct udevice *dev, struct abuf *buf);
+
+ /**
+ * set_cursor_visible() - Show or hide the cursor
+ *
+ * Shows or hides a cursor at the current position
+ *
+ * @dev: Console device to use
+ * @visible: true to show the cursor, false to hide it
+ * @x: X position in pixels
+ * @y: Y position in pixels
+ * @index: Character position (0 = at start)
+ * Return: 0 if OK, -ve on error
+ */
+ int (*set_cursor_visible)(struct udevice *dev, bool visible,
+ uint x, uint y, uint index);
};
/* Get a pointer to the driver operations for a video console device */
@@ -264,6 +324,60 @@
const char *text, struct vidconsole_bbox *bbox);
/**
+ * vidconsole_nominal() - Measure the expected width of a line of text
+ *
+ * Uses an average font width and nominal height
+ *
+ * @dev: Console device to use
+ * @name: Font name, NULL for default
+ * @size: Font size, ignored if @name is NULL
+ * @num_chars: Number of characters to use
+ * @bbox: Returns nounding box of @num_chars characters
+ * Returns: 0 if OK, -ve on error
+ */
+int vidconsole_nominal(struct udevice *dev, const char *name, uint size,
+ uint num_chars, struct vidconsole_bbox *bbox);
+
+/**
+ * vidconsole_entry_save() - Save any text-entry information for later use
+ *
+ * Saves text-entry context such as a list of positions for each
+ * character in the string.
+ *
+ * @dev: Console device to use
+ * @buf: Buffer to hold saved data
+ * Return: 0 if OK, -ENOMEM if out of memory
+ */
+int vidconsole_entry_save(struct udevice *dev, struct abuf *buf);
+
+/**
+ * entry_restore() - Restore text-entry information for current use
+ *
+ * Restores text-entry context such as a list of positions for each
+ * character in the string.
+ *
+ * @dev: Console device to use
+ * @buf: Buffer containing data to restore
+ * Return: 0 if OK, -ve on error
+ */
+int vidconsole_entry_restore(struct udevice *dev, struct abuf *buf);
+
+/**
+ * vidconsole_set_cursor_visible() - Show or hide the cursor
+ *
+ * Shows or hides a cursor at the current position
+ *
+ * @dev: Console device to use
+ * @visible: true to show the cursor, false to hide it
+ * @x: X position in pixels
+ * @y: Y position in pixels
+ * @index: Character position (0 = at start)
+ * Return: 0 if OK, -ve on error
+ */
+int vidconsole_set_cursor_visible(struct udevice *dev, bool visible,
+ uint x, uint y, uint index);
+
+/**
* vidconsole_push_colour() - Temporarily change the font colour
*
* @dev: Device to adjust
@@ -321,6 +435,15 @@
int vidconsole_set_row(struct udevice *dev, uint row, int clr);
/**
+ * vidconsole_entry_start() - Set the start position of a vidconsole line
+ *
+ * Marks the current cursor position as the start of a line
+ *
+ * @dev: Device to adjust
+ */
+int vidconsole_entry_start(struct udevice *dev);
+
+/**
* vidconsole_put_char() - Output a character to the current console position
*
* Outputs a character to the console and advances the cursor. This function
diff --git a/test/boot/cedit.c b/test/boot/cedit.c
index ab2b8a1..aa41719 100644
--- a/test/boot/cedit.c
+++ b/test/boot/cedit.c
@@ -58,6 +58,7 @@
/* Check the cedit write_fdt and read_fdt commands */
static int cedit_fdt(struct unit_test_state *uts)
{
+ struct scene_obj_textline *tline;
struct video_priv *vid_priv;
extern struct expo *cur_exp;
struct scene_obj_menu *menu;
@@ -66,6 +67,7 @@
struct scene *scn;
oftree tree;
ofnode node;
+ char *str;
void *fdt;
int i;
@@ -79,6 +81,12 @@
ut_assertnonnull(menu);
menu->cur_item_id = ID_CPU_SPEED_2;
+ /* get a textline to fiddle with too */
+ tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE);
+ ut_assertnonnull(tline);
+ str = abuf_data(&tline->buf);
+ strcpy(str, "my-machine");
+
ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0));
ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr));
ut_assert_nextlinen("1024 bytes read");
@@ -86,26 +94,29 @@
fdt = map_sysmem(addr, 1024);
tree = oftree_from_fdt(fdt);
node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME);
+ ut_assert(ofnode_valid(node));
ut_asserteq(ID_CPU_SPEED_2,
ofnode_read_u32_default(node, "cpu-speed", 0));
ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str"));
- ut_assert(ofnode_valid(node));
+ ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
- /* There should only be 4 properties */
+ /* There should only be 5 properties */
for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop);
i++, ofnode_next_property(&prop))
;
- ut_asserteq(4, i);
+ ut_asserteq(5, i);
ut_assert_console_end();
/* reset the expo */
menu->cur_item_id = ID_CPU_SPEED_1;
+ *str = '\0';
/* load in the settings and make sure they update */
ut_assertok(run_command("cedit read_fdt hostfs - settings.dtb", 0));
ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
+ ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
ut_assertnonnull(menu);
ut_assert_console_end();
@@ -117,10 +128,12 @@
/* Check the cedit write_env and read_env commands */
static int cedit_env(struct unit_test_state *uts)
{
+ struct scene_obj_textline *tline;
struct video_priv *vid_priv;
extern struct expo *cur_exp;
struct scene_obj_menu *menu;
struct scene *scn;
+ char *str;
console_record_reset_enable();
ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
@@ -132,25 +145,36 @@
ut_assertnonnull(menu);
menu->cur_item_id = ID_CPU_SPEED_2;
+ /* get a textline to fiddle with too */
+ tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE);
+ ut_assertnonnull(tline);
+ str = abuf_data(&tline->buf);
+ strcpy(str, "my-machine");
+
ut_assertok(run_command("cedit write_env -v", 0));
ut_assert_nextlinen("c.cpu-speed=7");
ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz");
ut_assert_nextlinen("c.power-loss=10");
ut_assert_nextlinen("c.power-loss-str=Always Off");
+ ut_assert_nextlinen("c.machine-name=my-machine");
ut_assert_console_end();
ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0));
ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str"));
+ ut_asserteq_str("my-machine", env_get("c.machine-name"));
/* reset the expo */
menu->cur_item_id = ID_CPU_SPEED_1;
+ *str = '\0';
ut_assertok(run_command("cedit read_env -v", 0));
ut_assert_nextlinen("c.cpu-speed=7");
ut_assert_nextlinen("c.power-loss=10");
+ ut_assert_nextlinen("c.machine-name=my-machine");
ut_assert_console_end();
ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
+ ut_asserteq_str("my-machine", env_get("c.machine-name"));
return 0;
}
diff --git a/test/boot/expo.c b/test/boot/expo.c
index 9002740..714fdfa 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -654,7 +654,7 @@
ut_asserteq_str("name", exp->name);
ut_asserteq(0, exp->scene_id);
- ut_asserteq(ID_DYNAMIC_START + 20, exp->next_id);
+ ut_asserteq(ID_DYNAMIC_START + 24, exp->next_id);
ut_asserteq(false, exp->popup);
/* check the scene */
diff --git a/test/boot/files/expo_ids.h b/test/boot/files/expo_ids.h
index 027d44b..a86e0d0 100644
--- a/test/boot/files/expo_ids.h
+++ b/test/boot/files/expo_ids.h
@@ -21,5 +21,8 @@
ID_AC_ON,
ID_AC_MEMORY,
+ ID_MACHINE_NAME,
+ ID_MACHINE_NAME_EDIT,
+
ID_DYNAMIC_START,
};
diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts
index cb2a674..bed5522 100644
--- a/test/boot/files/expo_layout.dts
+++ b/test/boot/files/expo_layout.dts
@@ -55,6 +55,14 @@
start-bit = <0x422>;
bit-length = <2>;
};
+
+ machine-name {
+ id = <ID_MACHINE_NAME>;
+ type = "textline";
+ max-chars = <20>;
+ title = "Machine name";
+ edit-id = <ID_MACHINE_NAME_EDIT>;
+ };
};
};
diff --git a/test/cmd/Makefile b/test/cmd/Makefile
index 6e3d7e9..8d70ac5 100644
--- a/test/cmd/Makefile
+++ b/test/cmd/Makefile
@@ -14,6 +14,7 @@
obj-$(CONFIG_CMD_BDI) += bdinfo.o
obj-$(CONFIG_CMD_FDT) += fdt.o
obj-$(CONFIG_CONSOLE_TRUETYPE) += font.o
+obj-$(CONFIG_CMD_HISTORY) += history.o
obj-$(CONFIG_CMD_LOADM) += loadm.o
obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o
ifdef CONFIG_CMD_PCI
diff --git a/test/cmd/history.c b/test/cmd/history.c
new file mode 100644
index 0000000..06517fc
--- /dev/null
+++ b/test/cmd/history.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for history command
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <cli.h>
+#include <command.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int lib_test_history(struct unit_test_state *uts)
+{
+ static const char cmd1[] = "setenv fred hello";
+ static const char cmd2[] = "print fred";
+
+ /* running commands directly does not add to history */
+ ut_assertok(run_command(cmd1, 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("history", 0));
+ ut_assert_console_end();
+
+ /* enter commands via the console */
+ console_in_puts(cmd1);
+ console_in_puts("\n");
+ ut_asserteq(strlen(cmd1), cli_readline(""));
+ ut_assert_nextline(cmd1);
+
+ console_in_puts(cmd2);
+ console_in_puts("\n");
+ ut_asserteq(strlen(cmd2), cli_readline(""));
+ ut_assert_nextline(cmd2);
+
+ ut_assertok(run_command("print fred", 0));
+ ut_assert_nextline("fred=hello");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("history", 0));
+ ut_assert_nextline(cmd1);
+ ut_assert_nextline(cmd2);
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_history, UT_TESTF_CONSOLE_REC);