2023-01-06 14:52:43 +00:00
|
|
|
.. SPDX-License-Identifier: GPL-2.0+
|
|
|
|
|
|
|
|
Expo menu
|
|
|
|
=========
|
|
|
|
|
|
|
|
U-Boot provides a menu implementation for use with selecting bootflows and
|
|
|
|
changing U-Boot settings. This is in early stages of development.
|
|
|
|
|
|
|
|
Motivation
|
|
|
|
----------
|
|
|
|
|
|
|
|
U-Boot already has a text-based menu system accessed via the
|
|
|
|
:doc:`../usage/cmd/bootmenu`. This works using environment variables, or via
|
|
|
|
some EFI-specific hacks.
|
|
|
|
|
|
|
|
The command makes use of a lower-level `menu` implementation, which is quite
|
|
|
|
flexible and can be used to make menu hierarchies.
|
|
|
|
|
|
|
|
However this system is not flexible enough for use with standard boot. It does
|
|
|
|
not support a graphical user interface and cannot currently support anything
|
|
|
|
more than a very simple list of items. While it does support multiple menus in
|
|
|
|
hierarchies, these are implemented by the caller. See for example `eficonfig.c`.
|
|
|
|
|
|
|
|
Another challenge with the current menu implementation is that it controls
|
|
|
|
the event loop, such that bootmenu_loop() does not return until a key is
|
|
|
|
pressed. This makes it difficult to implement dynamic displays or to do other
|
|
|
|
things while the menu is running, such as searching for more bootflows.
|
|
|
|
|
|
|
|
For these reasons an attempt has been made to develop a more flexible system
|
|
|
|
which can handle menus as well as other elements. This is called 'expo', short
|
|
|
|
for exposition, in an attempt to avoid common words like display, screen, menu
|
|
|
|
and the like. The primary goal is to support Verified Boot for Embedded (VBE),
|
|
|
|
although it is available to any boot method, using the 'bootflow menu' command.
|
|
|
|
|
|
|
|
Efforts have been made to use common code with the existing menu, including
|
|
|
|
key processing in particular.
|
|
|
|
|
|
|
|
Previous work looked at integrating Nuklear into U-Boot. This works fine and
|
|
|
|
could provide a way to provide a more flexible UI, perhaps with expo dealing
|
|
|
|
with the interface to Nuklear. But this is quite a big step and it may be years
|
|
|
|
before this becomes desirable, if at all. For now, U-Boot only needs a fairly
|
|
|
|
simple set of menus and options, so rendering them directly is fairly
|
|
|
|
straightforward.
|
|
|
|
|
|
|
|
Concepts
|
|
|
|
--------
|
|
|
|
|
|
|
|
The creator of the expo is here called a `controller` and it controls most
|
|
|
|
aspects of the expo. This is the code that you must write to use expo.
|
|
|
|
|
|
|
|
An `expo` is a set of scenes which can be presented to the user one at a time,
|
|
|
|
to show information and obtain input from the user.
|
|
|
|
|
|
|
|
A `scene` is a collection of objects which are displayed together on the screen.
|
|
|
|
Only one scene is visible at a time and scenes do not share objects.
|
|
|
|
|
|
|
|
A `scene object` is something that appears in the scene, such as some text, an
|
|
|
|
image or a menu. Objects can be positioned and hidden.
|
|
|
|
|
|
|
|
A `menu object` contains a title, a set of `menu items` and a pointer to the
|
|
|
|
current item. Menu items consist of a keypress (indicating what to press to
|
|
|
|
select the item), label and description. All three are shown in a single line
|
|
|
|
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.
|
|
|
|
|
|
|
|
While the expo implementation provides support for handling keypresses and
|
|
|
|
rendering on the display or serial port, it does not actually deal with reading
|
|
|
|
input from the user, nor what should be done when a particular menu item is
|
|
|
|
selected. This is deliberate since having the event loop outside the expo is
|
|
|
|
more flexible, particularly in a single-threaded environment like U-Boot.
|
|
|
|
|
|
|
|
Everything within an expo has a unique ID number. This is done so that it is
|
|
|
|
easy to refer to things after the expo has been created. The expectation is that
|
|
|
|
the controller declares an enum containing all of the elements in the expo,
|
|
|
|
passing the ID of each object as it is created. When a menu item is selected,
|
|
|
|
its ID is returned. When a object's font or position needs to change, the ID is
|
|
|
|
passed to expo functions to indicate which object it is. It is possible for expo
|
|
|
|
to auto-allocate IDs, but this is not recommended. The use of IDs is a
|
|
|
|
convenience, removing the need for the controller to store pointers to objects,
|
|
|
|
or even the IDs of objects. Programmatic creation of many items in a loop can be
|
|
|
|
handled by allocating space in the enum for a maximum number of items, then
|
|
|
|
adding the loop count to the enum values to obtain unique IDs.
|
|
|
|
|
2023-06-01 16:22:47 +00:00
|
|
|
Where dynamic IDs are need, use expo_set_dynamic_start() to set the start value,
|
|
|
|
so that they are allocated above the starting (enum) IDs.
|
|
|
|
|
2023-01-06 14:52:43 +00:00
|
|
|
All text strings are stored in a structure attached to the expo, referenced by
|
|
|
|
a text ID. This makes it easier at some point to implement multiple languages or
|
|
|
|
to support Unicode strings.
|
|
|
|
|
|
|
|
Menu objects do not have their own text and image objects. Instead they simply
|
|
|
|
refer to objects which have been created. So a menu item is just a collection
|
|
|
|
of IDs of text and image objects. When adding a menu item you must create these
|
|
|
|
objects first, then create the menu item, passing in the relevant IDs.
|
|
|
|
|
|
|
|
Creating an expo
|
|
|
|
----------------
|
|
|
|
|
2023-06-01 16:23:01 +00:00
|
|
|
To create an expo programmatically, use `expo_new()` followed by `scene_new()`
|
|
|
|
to create a scene. Then add objects to the scene, using functions like
|
|
|
|
`scene_txt_str()` and `scene_menu()`. For every menu item, add text and image
|
|
|
|
objects, then create the menu item with `scene_menuitem()`, referring to those
|
|
|
|
objects.
|
|
|
|
|
|
|
|
To create an expo using a description file, see :ref:`expo_format` below.
|
2023-01-06 14:52:43 +00:00
|
|
|
|
|
|
|
Layout
|
|
|
|
------
|
|
|
|
|
|
|
|
Individual objects can be positioned using `scene_obj_set_pos()`. Menu items
|
|
|
|
cannot be positioned manually: this is done by `scene_arrange()` which is called
|
|
|
|
automatically when something changes. The menu itself determines the position of
|
|
|
|
its items.
|
|
|
|
|
|
|
|
Rendering
|
|
|
|
---------
|
|
|
|
|
|
|
|
Rendering is performed by calling `expo_render()`. This uses either the
|
|
|
|
vidconsole, if present, or the serial console in `text mode`. Expo handles
|
|
|
|
presentation automatically in either case, without any change in how the expo is
|
|
|
|
created.
|
|
|
|
|
|
|
|
For the vidconsole, Truetype fonts can be used if enabled, to enhance the
|
|
|
|
quality of the display. For text mode, each menu item is shown in a single line,
|
|
|
|
allowing easy selection using arrow keys.
|
|
|
|
|
|
|
|
Input
|
|
|
|
-----
|
|
|
|
|
|
|
|
The controller is responsible for collecting keyboard input. A good way to do
|
|
|
|
this is to use `cli_ch_process()`, since it handles conversion of escape
|
|
|
|
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.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
Actions
|
|
|
|
-------
|
|
|
|
|
|
|
|
Call `expo_action_get()` in the event loop to check for any actions that the
|
|
|
|
expo wants to report. These can include selecting a particular menu item, or
|
|
|
|
quitting the menu. Processing of these is the responsibility of your controller.
|
|
|
|
|
|
|
|
Event loop
|
|
|
|
----------
|
|
|
|
|
|
|
|
Expo is intended to be used in an event loop. For an example loop, see
|
|
|
|
`bootflow_menu_run()`. It is possible to perform other work in your event loop,
|
|
|
|
such as scanning devices for more bootflows.
|
|
|
|
|
|
|
|
Themes
|
|
|
|
------
|
|
|
|
|
2023-06-01 16:22:53 +00:00
|
|
|
Expo supports simple themes, for setting the font size, for example. Use the
|
|
|
|
expo_apply_theme() function to load a theme, passing a node with the required
|
|
|
|
properties:
|
|
|
|
|
|
|
|
font-size
|
|
|
|
Font size to use for all text (type: u32)
|
|
|
|
|
2023-06-01 16:23:00 +00:00
|
|
|
menu-inset
|
|
|
|
Number of pixels to inset the menu on the sides and top (type: u32)
|
|
|
|
|
|
|
|
menuitem-gap-y
|
|
|
|
Number of pixels between menu items
|
|
|
|
|
2023-06-01 16:23:01 +00:00
|
|
|
Pop-up mode
|
|
|
|
-----------
|
|
|
|
|
|
|
|
Expos support two modes. The simple mode is used for selecting from a single
|
|
|
|
menu, e.g. when choosing with OS to boot. In this mode the menu items are shown
|
|
|
|
in a list (label, > pointer, key and description) and can be chosen using arrow
|
|
|
|
keys and enter::
|
|
|
|
|
|
|
|
U-Boot Boot Menu
|
|
|
|
|
|
|
|
UP and DOWN to choose, ENTER to select
|
|
|
|
|
|
|
|
mmc1 > 0 Fedora-Workstation-armhfp-31-1.9
|
|
|
|
mmc3 1 Armbian
|
|
|
|
|
|
|
|
The popup mode allows multiple menus to be present in a scene. Each is shown
|
|
|
|
just as its title and label, as with the `CPU Speed` and `AC Power` menus here::
|
|
|
|
|
|
|
|
Test Configuration
|
|
|
|
|
|
|
|
|
|
|
|
CPU Speed <2 GHz> (highlighted)
|
|
|
|
|
|
|
|
AC Power Always Off
|
|
|
|
|
|
|
|
|
|
|
|
UP and DOWN to choose, ENTER to select
|
|
|
|
|
|
|
|
|
2023-06-01 16:23:02 +00:00
|
|
|
.. _expo_format:
|
|
|
|
|
2023-06-01 16:23:01 +00:00
|
|
|
Expo Format
|
|
|
|
-----------
|
|
|
|
|
|
|
|
It can be tedious to create a complex expo using code. Expo supports a
|
|
|
|
data-driven approach, where the expo description is in a devicetree file. This
|
|
|
|
makes it easier and faster to create and edit the description. An expo builder
|
|
|
|
is provided to convert this format into an expo structure.
|
|
|
|
|
|
|
|
Layout of the expo scenes is handled automatically, based on a set of simple
|
2023-06-01 16:23:02 +00:00
|
|
|
rules. The :doc:`../usage/cmd/cedit` can be used to load a configuration
|
|
|
|
and create an expo from it.
|
2023-06-01 16:23:01 +00:00
|
|
|
|
|
|
|
Top-level node
|
|
|
|
~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
The top-level node has the following properties:
|
|
|
|
|
|
|
|
dynamic-start
|
|
|
|
type: u32, optional
|
|
|
|
|
|
|
|
Specifies the start of the dynamically allocated objects. This results in
|
|
|
|
a call to expo_set_dynamic_start().
|
|
|
|
|
|
|
|
The top-level node has the following subnodes:
|
|
|
|
|
|
|
|
scenes
|
|
|
|
Specifies the scenes in the expo, each one being a subnode
|
|
|
|
|
|
|
|
strings
|
|
|
|
Specifies the strings in the expo, each one being a subnode
|
|
|
|
|
|
|
|
`scenes` node
|
|
|
|
~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Contains a list of scene subnodes. The name of each subnode is passed as the
|
|
|
|
name to `scene_new()`.
|
|
|
|
|
|
|
|
`strings` node
|
|
|
|
~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Contains a list of string subnodes. The name of each subnode is ignored.
|
|
|
|
|
|
|
|
`strings` subnodes
|
|
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Each subnode defines a string which can be used by scenes and objects. Each
|
|
|
|
string has an ID number which is used to refer to it.
|
|
|
|
|
|
|
|
The `strings` subnodes have the following properties:
|
|
|
|
|
|
|
|
id
|
|
|
|
type: u32, required
|
|
|
|
|
|
|
|
Specifies the ID number for the string.
|
|
|
|
|
|
|
|
value:
|
|
|
|
type: string, required
|
|
|
|
|
|
|
|
Specifies the string text. For now only a single value is supported. Future
|
|
|
|
work may add support for multiple languages by using a value for each
|
|
|
|
language.
|
|
|
|
|
|
|
|
Scene nodes (`scenes` subnodes)
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Each subnode of the `scenes` node contains a scene description.
|
|
|
|
|
|
|
|
Most properties can use either a string or a string ID. For example, a `title`
|
|
|
|
property can be used to provide the title for a menu; alternatively a `title-id`
|
|
|
|
property can provide the string ID of the title. If both are present, the
|
|
|
|
ID takes preference, except that if a string with that ID does not exist, it
|
|
|
|
falls back to using the string from the property (`title` in this example). The
|
|
|
|
description below shows these are alternative properties with the same
|
|
|
|
description.
|
|
|
|
|
|
|
|
The scene nodes have the following properties:
|
|
|
|
|
|
|
|
id
|
|
|
|
type: u32, required
|
|
|
|
|
|
|
|
Specifies the ID number for the string.
|
|
|
|
|
|
|
|
title / title-id
|
|
|
|
type: string / u32, required
|
|
|
|
|
|
|
|
Specifies the title of the scene. This is shown at the top of the scene.
|
|
|
|
|
|
|
|
prompt / prompt-id
|
|
|
|
type: string / u32, required
|
|
|
|
|
|
|
|
Specifies a prompt for the scene. This is shown at the bottom of the scene.
|
|
|
|
|
|
|
|
The scene nodes have a subnode for each object in the scene.
|
|
|
|
|
|
|
|
Object nodes
|
|
|
|
~~~~~~~~~~~~
|
|
|
|
|
|
|
|
The object-node name is used as the name of the object, e.g. when calling
|
|
|
|
`scene_menu()` to create a menu.
|
|
|
|
|
|
|
|
Object nodes have the following common properties:
|
|
|
|
|
|
|
|
type
|
|
|
|
type: string, required
|
|
|
|
|
|
|
|
Specifies the type of the object. Valid types are:
|
|
|
|
|
|
|
|
"menu"
|
|
|
|
Menu containing items which can be selected by the user
|
|
|
|
|
|
|
|
id
|
|
|
|
type: u32, required
|
|
|
|
|
|
|
|
Specifies the ID of the object. This is used when referring to the object.
|
|
|
|
|
|
|
|
|
|
|
|
Menu nodes have the following additional properties:
|
|
|
|
|
|
|
|
title / title-id
|
|
|
|
type: string / u32, required
|
|
|
|
|
|
|
|
Specifies the title of the menu. This is shown to the left of the area for
|
|
|
|
this menu.
|
|
|
|
|
|
|
|
item-id
|
|
|
|
type: u32 list, required
|
|
|
|
|
|
|
|
Specifies the ID for each menu item. These are used for checking which item
|
|
|
|
has been selected.
|
|
|
|
|
|
|
|
item-label / item-label-id
|
|
|
|
type: string list / u32 list, required
|
|
|
|
|
|
|
|
Specifies the label for each item in the menu. These are shown to the user.
|
|
|
|
In 'popup' mode these form the items in the menu.
|
|
|
|
|
|
|
|
key-label / key-label-id
|
|
|
|
type: string list / u32 list, optional
|
|
|
|
|
|
|
|
Specifies the key for each item in the menu. These are currently only
|
|
|
|
intended for use in simple mode.
|
|
|
|
|
|
|
|
desc-label / desc-label-id
|
|
|
|
type: string list / u32 list, optional
|
|
|
|
|
|
|
|
Specifies the description for each item in the menu. These are currently
|
|
|
|
only intended for use in simple mode.
|
|
|
|
|
|
|
|
|
|
|
|
Expo layout
|
|
|
|
~~~~~~~~~~~
|
|
|
|
|
|
|
|
The `expo_arrange()` function can be called to arrange the expo objects in a
|
|
|
|
suitable manner. For each scene it puts the title at the top, the prompt at the
|
|
|
|
bottom and the objects in order from top to bottom.
|
|
|
|
|
|
|
|
Expo format example
|
|
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
This example shows an expo with a single scene consisting of two menus. The
|
|
|
|
scene title is specified using a string from the strings table, but all other
|
|
|
|
strings are provided inline in the nodes where they are used.
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
#define ID_PROMPT 1
|
|
|
|
#define ID_SCENE1 2
|
|
|
|
#define ID_SCENE1_TITLE 3
|
|
|
|
|
|
|
|
#define ID_CPU_SPEED 4
|
|
|
|
#define ID_CPU_SPEED_TITLE 5
|
|
|
|
#define ID_CPU_SPEED_1 6
|
|
|
|
#define ID_CPU_SPEED_2 7
|
|
|
|
#define ID_CPU_SPEED_3 8
|
|
|
|
|
|
|
|
#define ID_POWER_LOSS 9
|
|
|
|
#define ID_AC_OFF 10
|
|
|
|
#define ID_AC_ON 11
|
|
|
|
#define ID_AC_MEMORY 12
|
|
|
|
|
|
|
|
#define ID_DYNAMIC_START 13
|
|
|
|
|
|
|
|
&cedit {
|
|
|
|
dynamic-start = <ID_DYNAMIC_START>;
|
|
|
|
|
|
|
|
scenes {
|
|
|
|
main {
|
|
|
|
id = <ID_SCENE1>;
|
|
|
|
|
|
|
|
/* value refers to the matching id in /strings */
|
|
|
|
title-id = <ID_SCENE1_TITLE>;
|
|
|
|
|
|
|
|
/* simple string is used as it is */
|
|
|
|
prompt = "UP and DOWN to choose, ENTER to select";
|
|
|
|
|
|
|
|
/* defines a menu within the scene */
|
|
|
|
cpu-speed {
|
|
|
|
type = "menu";
|
|
|
|
id = <ID_CPU_SPEED>;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* has both string and ID. The string is ignored
|
|
|
|
* if the ID is present and points to a string
|
|
|
|
*/
|
|
|
|
title = "CPU speed";
|
|
|
|
title-id = <ID_CPU_SPEED_TITLE>;
|
|
|
|
|
|
|
|
/* menu items as simple strings */
|
|
|
|
item-label = "2 GHz", "2.5 GHz", "3 GHz";
|
|
|
|
|
|
|
|
/* IDs for the menu items */
|
|
|
|
item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
|
|
|
|
ID_CPU_SPEED_3>;
|
|
|
|
};
|
|
|
|
|
|
|
|
power-loss {
|
|
|
|
type = "menu";
|
|
|
|
id = <ID_POWER_LOSS>;
|
|
|
|
|
|
|
|
title = "AC Power";
|
|
|
|
item-label = "Always Off", "Always On",
|
|
|
|
"Memory";
|
|
|
|
|
|
|
|
item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
strings {
|
|
|
|
title {
|
|
|
|
id = <ID_SCENE1_TITLE>;
|
|
|
|
value = "Test Configuration";
|
|
|
|
value-es = "configuración de prueba";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-01-06 14:52:43 +00:00
|
|
|
|
|
|
|
API documentation
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
.. kernel-doc:: include/expo.h
|
|
|
|
|
|
|
|
Future ideas
|
|
|
|
------------
|
|
|
|
|
|
|
|
Some ideas for future work:
|
|
|
|
|
|
|
|
- Default menu item and a timeout
|
|
|
|
- Image formats other than BMP
|
|
|
|
- Use of ANSI sequences to control a serial terminal
|
|
|
|
- Colour selection
|
2023-06-01 16:23:01 +00:00
|
|
|
- Support for more widgets, e.g. text, numeric, radio/option
|
2023-01-06 14:52:43 +00:00
|
|
|
- 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
|
|
|
|
- Use expo to replace the existing menu implementation
|
|
|
|
- Add a Kconfig option to drop the names to save code / data space
|
|
|
|
- Add a Kconfig option to disable vidconsole support to save code / data space
|
|
|
|
- Support both graphical and text menus at the same time on different devices
|
|
|
|
- Support unicode
|
|
|
|
- Support curses for proper serial-terminal menus
|
2023-06-01 16:23:01 +00:00
|
|
|
- Add support for large menus which need to scroll
|
2023-06-01 16:23:02 +00:00
|
|
|
- Add support for reading and writing configuration settings with cedit
|
2023-01-06 14:52:43 +00:00
|
|
|
|
|
|
|
.. Simon Glass <sjg@chromium.org>
|
|
|
|
.. 7-Oct-22
|