Simon Glass | 311bd35 | 2023-01-06 08:52:43 -0600 | [diff] [blame] | 1 | .. SPDX-License-Identifier: GPL-2.0+ |
| 2 | |
| 3 | Expo menu |
| 4 | ========= |
| 5 | |
| 6 | U-Boot provides a menu implementation for use with selecting bootflows and |
| 7 | changing U-Boot settings. This is in early stages of development. |
| 8 | |
| 9 | Motivation |
| 10 | ---------- |
| 11 | |
| 12 | U-Boot already has a text-based menu system accessed via the |
| 13 | :doc:`../usage/cmd/bootmenu`. This works using environment variables, or via |
| 14 | some EFI-specific hacks. |
| 15 | |
| 16 | The command makes use of a lower-level `menu` implementation, which is quite |
| 17 | flexible and can be used to make menu hierarchies. |
| 18 | |
| 19 | However this system is not flexible enough for use with standard boot. It does |
| 20 | not support a graphical user interface and cannot currently support anything |
| 21 | more than a very simple list of items. While it does support multiple menus in |
| 22 | hierarchies, these are implemented by the caller. See for example `eficonfig.c`. |
| 23 | |
| 24 | Another challenge with the current menu implementation is that it controls |
| 25 | the event loop, such that bootmenu_loop() does not return until a key is |
| 26 | pressed. This makes it difficult to implement dynamic displays or to do other |
| 27 | things while the menu is running, such as searching for more bootflows. |
| 28 | |
| 29 | For these reasons an attempt has been made to develop a more flexible system |
| 30 | which can handle menus as well as other elements. This is called 'expo', short |
| 31 | for exposition, in an attempt to avoid common words like display, screen, menu |
| 32 | and the like. The primary goal is to support Verified Boot for Embedded (VBE), |
| 33 | although it is available to any boot method, using the 'bootflow menu' command. |
| 34 | |
| 35 | Efforts have been made to use common code with the existing menu, including |
| 36 | key processing in particular. |
| 37 | |
| 38 | Previous work looked at integrating Nuklear into U-Boot. This works fine and |
| 39 | could provide a way to provide a more flexible UI, perhaps with expo dealing |
| 40 | with the interface to Nuklear. But this is quite a big step and it may be years |
| 41 | before this becomes desirable, if at all. For now, U-Boot only needs a fairly |
| 42 | simple set of menus and options, so rendering them directly is fairly |
| 43 | straightforward. |
| 44 | |
| 45 | Concepts |
| 46 | -------- |
| 47 | |
| 48 | The creator of the expo is here called a `controller` and it controls most |
| 49 | aspects of the expo. This is the code that you must write to use expo. |
| 50 | |
| 51 | An `expo` is a set of scenes which can be presented to the user one at a time, |
| 52 | to show information and obtain input from the user. |
| 53 | |
| 54 | A `scene` is a collection of objects which are displayed together on the screen. |
| 55 | Only one scene is visible at a time and scenes do not share objects. |
| 56 | |
| 57 | A `scene object` is something that appears in the scene, such as some text, an |
| 58 | image or a menu. Objects can be positioned and hidden. |
| 59 | |
| 60 | A `menu object` contains a title, a set of `menu items` and a pointer to the |
| 61 | current item. Menu items consist of a keypress (indicating what to press to |
| 62 | select the item), label and description. All three are shown in a single line |
| 63 | within the menu. Items can also have a preview image, which is shown when the |
| 64 | item is highlighted. |
| 65 | |
| 66 | All components have a name. This is purely for debugging, so it is easy to see |
| 67 | what object is referred to. Of course the ID numbers can help as well, but they |
| 68 | are less easy to distinguish. |
| 69 | |
| 70 | While the expo implementation provides support for handling keypresses and |
| 71 | rendering on the display or serial port, it does not actually deal with reading |
| 72 | input from the user, nor what should be done when a particular menu item is |
| 73 | selected. This is deliberate since having the event loop outside the expo is |
| 74 | more flexible, particularly in a single-threaded environment like U-Boot. |
| 75 | |
| 76 | Everything within an expo has a unique ID number. This is done so that it is |
| 77 | easy to refer to things after the expo has been created. The expectation is that |
| 78 | the controller declares an enum containing all of the elements in the expo, |
| 79 | passing the ID of each object as it is created. When a menu item is selected, |
| 80 | its ID is returned. When a object's font or position needs to change, the ID is |
| 81 | passed to expo functions to indicate which object it is. It is possible for expo |
| 82 | to auto-allocate IDs, but this is not recommended. The use of IDs is a |
| 83 | convenience, removing the need for the controller to store pointers to objects, |
| 84 | or even the IDs of objects. Programmatic creation of many items in a loop can be |
| 85 | handled by allocating space in the enum for a maximum number of items, then |
| 86 | adding the loop count to the enum values to obtain unique IDs. |
| 87 | |
| 88 | All text strings are stored in a structure attached to the expo, referenced by |
| 89 | a text ID. This makes it easier at some point to implement multiple languages or |
| 90 | to support Unicode strings. |
| 91 | |
| 92 | Menu objects do not have their own text and image objects. Instead they simply |
| 93 | refer to objects which have been created. So a menu item is just a collection |
| 94 | of IDs of text and image objects. When adding a menu item you must create these |
| 95 | objects first, then create the menu item, passing in the relevant IDs. |
| 96 | |
| 97 | Creating an expo |
| 98 | ---------------- |
| 99 | |
| 100 | To create an expo, use `expo_new()` followed by `scene_new()` to create a scene. |
| 101 | Then add objects to the scene, using functions like `scene_txt_str()` and |
| 102 | `scene_menu()`. For every menu item, add text and image objects, then create |
| 103 | the menu item with `scene_menuitem()`, referring to those objects. |
| 104 | |
| 105 | Layout |
| 106 | ------ |
| 107 | |
| 108 | Individual objects can be positioned using `scene_obj_set_pos()`. Menu items |
| 109 | cannot be positioned manually: this is done by `scene_arrange()` which is called |
| 110 | automatically when something changes. The menu itself determines the position of |
| 111 | its items. |
| 112 | |
| 113 | Rendering |
| 114 | --------- |
| 115 | |
| 116 | Rendering is performed by calling `expo_render()`. This uses either the |
| 117 | vidconsole, if present, or the serial console in `text mode`. Expo handles |
| 118 | presentation automatically in either case, without any change in how the expo is |
| 119 | created. |
| 120 | |
| 121 | For the vidconsole, Truetype fonts can be used if enabled, to enhance the |
| 122 | quality of the display. For text mode, each menu item is shown in a single line, |
| 123 | allowing easy selection using arrow keys. |
| 124 | |
| 125 | Input |
| 126 | ----- |
| 127 | |
| 128 | The controller is responsible for collecting keyboard input. A good way to do |
| 129 | this is to use `cli_ch_process()`, since it handles conversion of escape |
| 130 | sequences into keys. However, expo has some special menu-key codes for |
| 131 | navigating the interface. These are defined in `enum bootmenu_key` and include |
| 132 | `BKEY_UP` for moving up and `BKEY_SELECT` for selecting an item. You can use |
| 133 | `bootmenu_conv_key()` to convert an ASCII key into one of these. |
| 134 | |
| 135 | Once a keypress is decoded, call `expo_send_key()` to send it to the expo. This |
| 136 | may cause an update to the expo state and may produce an action. |
| 137 | |
| 138 | Actions |
| 139 | ------- |
| 140 | |
| 141 | Call `expo_action_get()` in the event loop to check for any actions that the |
| 142 | expo wants to report. These can include selecting a particular menu item, or |
| 143 | quitting the menu. Processing of these is the responsibility of your controller. |
| 144 | |
| 145 | Event loop |
| 146 | ---------- |
| 147 | |
| 148 | Expo is intended to be used in an event loop. For an example loop, see |
| 149 | `bootflow_menu_run()`. It is possible to perform other work in your event loop, |
| 150 | such as scanning devices for more bootflows. |
| 151 | |
| 152 | Themes |
| 153 | ------ |
| 154 | |
| 155 | Expo does not itself support themes. The bootflow_menu implement supposed a |
| 156 | basic theme, applying font sizes to the various text objects in the expo. |
| 157 | |
| 158 | API documentation |
| 159 | ----------------- |
| 160 | |
| 161 | .. kernel-doc:: include/expo.h |
| 162 | |
| 163 | Future ideas |
| 164 | ------------ |
| 165 | |
| 166 | Some ideas for future work: |
| 167 | |
| 168 | - Default menu item and a timeout |
| 169 | - Higher-level / automatic / more flexible layout of objects |
| 170 | - Image formats other than BMP |
| 171 | - Use of ANSI sequences to control a serial terminal |
| 172 | - Colour selection |
| 173 | - Better support for handling lots of settings, e.g. with multiple menus and |
| 174 | radio/option widgets |
| 175 | - Mouse support |
| 176 | - Integrate Nuklear, NxWidgets or some other library for a richer UI |
| 177 | - Optimise rendering by only updating the display with changes since last render |
| 178 | - Use expo to replace the existing menu implementation |
| 179 | - Add a Kconfig option to drop the names to save code / data space |
| 180 | - Add a Kconfig option to disable vidconsole support to save code / data space |
| 181 | - Support both graphical and text menus at the same time on different devices |
| 182 | - Implement proper measurement of object bounding boxes, to permit more exact |
| 183 | layout. This would tidy up the layout when Truetype is not used |
| 184 | - Support unicode |
| 185 | - Support curses for proper serial-terminal menus |
| 186 | |
| 187 | .. Simon Glass <sjg@chromium.org> |
| 188 | .. 7-Oct-22 |