1 // SPDX-License-Identifier: GPL-2.0+
3 * Implementation of a menu in a scene
5 * Copyright 2022 Google LLC
9 #define LOG_CATEGORY LOGC_BOOT
18 #include <video_console.h>
19 #include <linux/input.h>
20 #include "scene_internal.h"
22 static void scene_menuitem_destroy(struct scene_menitem *item)
28 void scene_menu_destroy(struct scene_obj_menu *menu)
30 struct scene_menitem *item, *next;
32 list_for_each_entry_safe(item, next, &menu->item_head, sibling)
33 scene_menuitem_destroy(item);
37 * menu_point_to_item() - Point to a particular menu item
39 * Sets the currently pointed-to / highlighted menu item
41 static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id)
43 menu->cur_item_id = item_id;
46 int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
48 struct scene_menitem *item;
54 ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.x, y);
56 return log_msg_ret("tit", ret);
58 ret = scene_obj_get_hw(scn, menu->title_id, NULL);
60 return log_msg_ret("hei", ret);
66 * Currently everything is hard-coded to particular columns so this
67 * won't work on small displays and looks strange if the font size is
68 * small. This can be updated once text measuring is supported in
72 list_for_each_entry(item, &menu->item_head, sibling) {
75 ret = scene_obj_get_hw(scn, item->desc_id, NULL);
77 return log_msg_ret("get", ret);
80 if (item->flags & SCENEMIF_GAP_BEFORE)
83 /* select an item if not done already */
84 if (!menu->cur_item_id)
85 menu_point_to_item(menu, item->id);
88 * Put the label on the left, then leave a space for the
89 * pointer, then the key and the description
92 ret = scene_obj_set_pos(scn, item->label_id, menu->obj.x,
95 return log_msg_ret("nam", ret);
98 ret = scene_obj_set_pos(scn, item->key_id, menu->obj.x + 230,
101 return log_msg_ret("key", ret);
103 ret = scene_obj_set_pos(scn, item->desc_id, menu->obj.x + 280,
106 return log_msg_ret("des", ret);
108 if (menu->cur_item_id == item->id)
111 if (item->preview_id) {
115 * put all previews on top of each other, on the right
116 * size of the display
118 ret = scene_obj_set_pos(scn, item->preview_id, -4, y);
120 return log_msg_ret("prev", ret);
122 hide = menu->cur_item_id != item->id;
123 ret = scene_obj_set_hide(scn, item->preview_id, hide);
125 return log_msg_ret("hid", ret);
131 if (menu->pointer_id && cur_y != -1) {
133 * put the pointer to the right of and level with the item it
136 ret = scene_obj_set_pos(scn, menu->pointer_id,
137 menu->obj.x + 200, cur_y);
139 return log_msg_ret("ptr", ret);
145 int scene_menu(struct scene *scn, const char *name, uint id,
146 struct scene_obj_menu **menup)
148 struct scene_obj_menu *menu;
151 ret = scene_obj_add(scn, name, id, SCENEOBJT_MENU,
152 sizeof(struct scene_obj_menu),
153 (struct scene_obj **)&menu);
155 return log_msg_ret("obj", -ENOMEM);
159 INIT_LIST_HEAD(&menu->item_head);
161 ret = scene_menu_arrange(scn, menu);
163 return log_msg_ret("pos", ret);
168 static struct scene_menitem *scene_menu_find_key(struct scene *scn,
169 struct scene_obj_menu *menu,
172 struct scene_menitem *item;
174 list_for_each_entry(item, &menu->item_head, sibling) {
176 struct scene_obj_txt *txt;
179 txt = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
181 str = expo_get_str(scn->expo, txt->str_id);
182 if (str && *str == key)
191 int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
192 struct expo_action *event)
194 struct scene_menitem *item, *cur, *key_item;
199 if (!list_empty(&menu->item_head)) {
200 list_for_each_entry(item, &menu->item_head, sibling) {
201 /* select an item if not done already */
202 if (menu->cur_item_id == item->id) {
214 if (item != list_first_entry(&menu->item_head,
215 struct scene_menitem, sibling)) {
216 item = list_entry(item->sibling.prev,
217 struct scene_menitem, sibling);
218 event->type = EXPOACT_POINT;
219 event->select.id = item->id;
220 log_debug("up to item %d\n", event->select.id);
224 if (!list_is_last(&item->sibling, &menu->item_head)) {
225 item = list_entry(item->sibling.next,
226 struct scene_menitem, sibling);
227 event->type = EXPOACT_POINT;
228 event->select.id = item->id;
229 log_debug("down to item %d\n", event->select.id);
233 event->type = EXPOACT_SELECT;
234 event->select.id = item->id;
235 log_debug("select item %d\n", event->select.id);
238 event->type = EXPOACT_QUIT;
242 key_item = scene_menu_find_key(scn, menu, key);
244 event->type = EXPOACT_SELECT;
245 event->select.id = key_item->id;
250 menu_point_to_item(menu, item->id);
255 int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
256 uint key_id, uint label_id, uint desc_id, uint preview_id,
257 uint flags, struct scene_menitem **itemp)
259 struct scene_obj_menu *menu;
260 struct scene_menitem *item;
263 menu = scene_obj_find(scn, menu_id, SCENEOBJT_MENU);
265 return log_msg_ret("find", -ENOENT);
267 /* Check that the text ID is valid */
268 if (!scene_obj_find(scn, desc_id, SCENEOBJT_TEXT))
269 return log_msg_ret("txt", -EINVAL);
271 item = calloc(1, sizeof(struct scene_obj_menu));
273 return log_msg_ret("item", -ENOMEM);
274 item->name = strdup(name);
277 return log_msg_ret("name", -ENOMEM);
280 item->id = resolve_id(scn->expo, id);
281 item->key_id = key_id;
282 item->label_id = label_id;
283 item->desc_id = desc_id;
284 item->preview_id = preview_id;
286 list_add_tail(&item->sibling, &menu->item_head);
288 ret = scene_menu_arrange(scn, menu);
290 return log_msg_ret("pos", ret);
298 int scene_menu_set_title(struct scene *scn, uint id, uint title_id)
300 struct scene_obj_menu *menu;
301 struct scene_obj_txt *txt;
303 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
305 return log_msg_ret("menu", -ENOENT);
307 /* Check that the ID is valid */
309 txt = scene_obj_find(scn, title_id, SCENEOBJT_TEXT);
311 return log_msg_ret("txt", -EINVAL);
314 menu->title_id = title_id;
319 int scene_menu_set_pointer(struct scene *scn, uint id, uint pointer_id)
321 struct scene_obj_menu *menu;
322 struct scene_obj *obj;
324 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
326 return log_msg_ret("menu", -ENOENT);
328 /* Check that the ID is valid */
330 obj = scene_obj_find(scn, pointer_id, SCENEOBJT_NONE);
332 return log_msg_ret("obj", -EINVAL);
335 menu->pointer_id = pointer_id;
340 int scene_menu_display(struct scene_obj_menu *menu)
342 struct scene *scn = menu->obj.scene;
343 struct scene_obj_txt *pointer;
344 struct expo *exp = scn->expo;
345 struct scene_menitem *item;
348 printf("U-Boot : Boot Menu\n\n");
349 if (menu->title_id) {
350 struct scene_obj_txt *txt;
353 txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_TEXT);
355 return log_msg_ret("txt", -EINVAL);
357 str = expo_get_str(exp, txt->str_id);
358 printf("%s\n\n", str);
361 if (list_empty(&menu->item_head))
364 pointer = scene_obj_find(scn, menu->pointer_id, SCENEOBJT_TEXT);
365 pstr = expo_get_str(scn->expo, pointer->str_id);
367 list_for_each_entry(item, &menu->item_head, sibling) {
368 struct scene_obj_txt *key = NULL, *label = NULL;
369 struct scene_obj_txt *desc = NULL;
370 const char *kstr = NULL, *lstr = NULL, *dstr = NULL;
372 key = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
374 kstr = expo_get_str(exp, key->str_id);
376 label = scene_obj_find(scn, item->label_id, SCENEOBJT_TEXT);
378 lstr = expo_get_str(exp, label->str_id);
380 desc = scene_obj_find(scn, item->desc_id, SCENEOBJT_TEXT);
382 dstr = expo_get_str(exp, desc->str_id);
384 printf("%3s %3s %-10s %s\n",
385 pointer && menu->cur_item_id == item->id ? pstr : "",