bootstd: Support setting a theme for the menu

Allow a theme to be set. For now this is very simple, just a default font
size to use for all elements.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c
index 1105783..7f06dac 100644
--- a/boot/bootflow_menu.c
+++ b/boot/bootflow_menu.c
@@ -129,6 +129,43 @@
 	return 0;
 }
 
+int bootflow_menu_apply_theme(struct expo *exp, ofnode node)
+{
+	struct menu_priv *priv = exp->priv;
+	struct scene *scn;
+	u32 font_size;
+	int ret;
+
+	log_debug("Applying theme %s\n", ofnode_get_name(node));
+	scn = expo_lookup_scene_id(exp, MAIN);
+	if (!scn)
+		return log_msg_ret("scn", -ENOENT);
+
+	/* Avoid error-checking optional items */
+	if (!ofnode_read_u32(node, "font-size", &font_size)) {
+		int i;
+
+		log_debug("font size %d\n", font_size);
+		scene_txt_set_font(scn, OBJ_PROMPT, NULL, font_size);
+		scene_txt_set_font(scn, OBJ_POINTER, NULL, font_size);
+		for (i = 0; i < priv->num_bootflows; i++) {
+			ret = scene_txt_set_font(scn, ITEM_DESC + i, NULL,
+						 font_size);
+			if (ret)
+				return log_msg_ret("des", ret);
+			scene_txt_set_font(scn, ITEM_KEY + i, NULL, font_size);
+			scene_txt_set_font(scn, ITEM_LABEL + i, NULL,
+					   font_size);
+		}
+	}
+
+	ret = scene_arrange(scn);
+	if (ret)
+		return log_msg_ret("arr", ret);
+
+	return 0;
+}
+
 int bootflow_menu_run(struct bootstd_priv *std, bool text_mode,
 		      struct bootflow **bflowp)
 {
@@ -149,6 +186,12 @@
 	if (ret)
 		return log_msg_ret("exp", ret);
 
+	if (ofnode_valid(std->theme)) {
+		ret = bootflow_menu_apply_theme(exp, std->theme);
+		if (ret)
+			return log_msg_ret("thm", ret);
+	}
+
 	/* For now we only support a video console */
 	ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
 	if (ret)
diff --git a/boot/bootstd-uclass.c b/boot/bootstd-uclass.c
index 565c22a..7887acd 100644
--- a/boot/bootstd-uclass.c
+++ b/boot/bootstd-uclass.c
@@ -33,6 +33,8 @@
 					   &priv->prefixes);
 		dev_read_string_list(dev, "bootdev-order",
 				     &priv->bootdev_order);
+
+		priv->theme = ofnode_find_subnode(dev_ofnode(dev), "theme");
 	}
 
 	return 0;
diff --git a/include/bootflow.h b/include/bootflow.h
index e7a0956..c201246 100644
--- a/include/bootflow.h
+++ b/include/bootflow.h
@@ -7,6 +7,7 @@
 #ifndef __bootflow_h
 #define __bootflow_h
 
+#include <dm/ofnode_decl.h>
 #include <linux/list.h>
 
 struct bootstd_priv;
@@ -348,6 +349,15 @@
 int bootflow_menu_new(struct expo **expp);
 
 /**
+ * bootflow_menu_apply_theme() - Apply a theme to a bootmenu
+ *
+ * @exp: Expo to update
+ * @node: Node containing the theme information
+ * Returns 0 on success, -ve on error
+ */
+int bootflow_menu_apply_theme(struct expo *exp, ofnode node);
+
+/**
  * bootflow_menu_run() - Create and run a menu of available bootflows
  *
  * @std: Bootstd information
diff --git a/include/bootstd.h b/include/bootstd.h
index 01be249..4fa0d53 100644
--- a/include/bootstd.h
+++ b/include/bootstd.h
@@ -9,6 +9,8 @@
 #ifndef __bootstd_h
 #define __bootstd_h
 
+#include <dm/ofnode_decl.h>
+
 struct udevice;
 
 /**
@@ -27,6 +29,7 @@
  * @bootmeth_count: Number of bootmeth devices in @bootmeth_order
  * @bootmeth_order: List of bootmeth devices to use, in order, NULL-terminated
  * @vbe_bootmeth: Currently selected VBE bootmeth, NULL if none
+ * @theme: Node containing the theme information
  */
 struct bootstd_priv {
 	const char **prefixes;
@@ -37,6 +40,7 @@
 	int bootmeth_count;
 	struct udevice **bootmeth_order;
 	struct udevice *vbe_bootmeth;
+	ofnode theme;
 };
 
 /**
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
index abafa44..5b76cd3 100644
--- a/test/boot/bootflow.c
+++ b/test/boot/bootflow.c
@@ -13,6 +13,7 @@
 #include <bootstd.h>
 #include <cli.h>
 #include <dm.h>
+#include <expo.h>
 #ifdef CONFIG_SANDBOX
 #include <asm/test.h>
 #endif
@@ -21,6 +22,8 @@
 #include <test/suites.h>
 #include <test/ut.h>
 #include "bootstd_common.h"
+#include "../../boot/bootflow_internal.h"
+#include "../../boot/scene_internal.h"
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -469,14 +472,18 @@
 }
 BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
 
-/* Check 'bootflow menu' to select a bootflow */
-static int bootflow_cmd_menu(struct unit_test_state *uts)
+/**
+ * prep_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian
+ *
+ * @uts: Unit test state
+ * Returns 0 on success, -ve on failure
+ */
+static int prep_mmc4_bootdev(struct unit_test_state *uts)
 {
 	static const char *order[] = {"mmc2", "mmc1", "mmc4", NULL};
 	struct udevice *dev, *bootstd;
 	struct bootstd_priv *std;
 	const char **old_order;
-	char prev[3];
 	ofnode node;
 
 	/* Enable the mmc4 node since we need a second bootflow */
@@ -500,6 +507,16 @@
 	/* Restore the order used by the device tree */
 	std->bootdev_order = old_order;
 
+	return 0;
+}
+
+/* Check 'bootflow menu' to select a bootflow */
+static int bootflow_cmd_menu(struct unit_test_state *uts)
+{
+	char prev[3];
+
+	ut_assertok(prep_mmc4_bootdev(uts));
+
 	/* Add keypresses to move to and select the second one in the list */
 	prev[0] = CTL_CH('n');
 	prev[1] = '\r';
@@ -513,3 +530,63 @@
 	return 0;
 }
 BOOTSTD_TEST(bootflow_cmd_menu, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/**
+ * check_font() - Check that the font size for an item matches expectations
+ *
+ * @uts: Unit test state
+ * @scn: Scene containing the text object
+ * @id: ID of the text object
+ * Returns 0 on success, -ve on failure
+ */
+static int check_font(struct unit_test_state *uts, struct scene *scn, uint id,
+		      int font_size)
+{
+	struct scene_obj_txt *txt;
+
+	txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
+	ut_assertnonnull(txt);
+
+	ut_asserteq(font_size, txt->font_size);
+
+	return 0;
+}
+
+/* Check themes work with a bootflow menu */
+static int bootflow_menu_theme(struct unit_test_state *uts)
+{
+	const int font_size = 30;
+	struct scene *scn;
+	struct expo *exp;
+	ofnode node;
+	int i;
+
+	ut_assertok(prep_mmc4_bootdev(uts));
+
+	ut_assertok(bootflow_menu_new(&exp));
+	node = ofnode_path("/bootstd/theme");
+	ut_assert(ofnode_valid(node));
+	ut_assertok(bootflow_menu_apply_theme(exp, node));
+
+	scn = expo_lookup_scene_id(exp, MAIN);
+	ut_assertnonnull(scn);
+
+	/*
+	 * Check that the txt objects have the correct font size from the
+	 * device tree node: bootstd/theme
+	 *
+	 * Check both menu items, since there are two bootflows
+	 */
+	ut_assertok(check_font(uts, scn, OBJ_PROMPT, font_size));
+	ut_assertok(check_font(uts, scn, OBJ_POINTER, font_size));
+	for (i = 0; i < 2; i++) {
+		ut_assertok(check_font(uts, scn, ITEM_DESC + i, font_size));
+		ut_assertok(check_font(uts, scn, ITEM_KEY + i, font_size));
+		ut_assertok(check_font(uts, scn, ITEM_LABEL + i, font_size));
+	}
+
+	expo_destroy(exp);
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_menu_theme, UT_TESTF_DM | UT_TESTF_SCAN_FDT);