]> Git Repo - J-u-boot.git/blame - boot/scene_menu.c
expo: Add spacing around menus and items
[J-u-boot.git] / boot / scene_menu.c
CommitLineData
226777f1
SG
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Implementation of a menu in a scene
4 *
5 * Copyright 2022 Google LLC
6 * Written by Simon Glass <[email protected]>
7 */
8
c98cb512 9#define LOG_CATEGORY LOGC_EXPO
226777f1
SG
10
11#include <common.h>
12#include <dm.h>
13#include <expo.h>
14#include <malloc.h>
15#include <mapmem.h>
16#include <menu.h>
17#include <video.h>
18#include <video_console.h>
19#include <linux/input.h>
20#include "scene_internal.h"
21
22static void scene_menuitem_destroy(struct scene_menitem *item)
23{
24 free(item->name);
25 free(item);
26}
27
28void scene_menu_destroy(struct scene_obj_menu *menu)
29{
30 struct scene_menitem *item, *next;
31
32 list_for_each_entry_safe(item, next, &menu->item_head, sibling)
33 scene_menuitem_destroy(item);
34}
35
8872bc7f
SG
36static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu,
37 int id)
38{
39 struct scene_menitem *item;
40
41 list_for_each_entry(item, &menu->item_head, sibling) {
42 if (item->id == id)
43 return item;
44 }
45
46 return NULL;
47}
48
49/**
50 * update_pointers() - Update the pointer object and handle highlights
51 *
52 * @menu: Menu to update
53 * @id: ID of menu item to select/deselect
54 * @point: true if @id is being selected, false if it is being deselected
55 */
56static int update_pointers(struct scene_obj_menu *menu, uint id, bool point)
57{
58 struct scene *scn = menu->obj.scene;
d3db0216 59 const bool stack = scn->expo->popup;
8872bc7f
SG
60 const struct scene_menitem *item;
61 int ret;
62
63 item = scene_menuitem_find(menu, id);
64 if (!item)
65 return log_msg_ret("itm", -ENOENT);
66
67 /* adjust the pointer object to point to the selected item */
68 if (menu->pointer_id && item && point) {
69 struct scene_obj *label;
70
71 label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
72
73 ret = scene_obj_set_pos(scn, menu->pointer_id,
74 menu->obj.dim.x + 200, label->dim.y);
75 if (ret < 0)
76 return log_msg_ret("ptr", ret);
77 }
78
d3db0216
SG
79 if (stack) {
80 point &= scn->highlight_id == menu->obj.id;
81 scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT,
82 point ? SCENEOF_POINT : 0);
83 }
84
8872bc7f
SG
85 return 0;
86}
87
226777f1
SG
88/**
89 * menu_point_to_item() - Point to a particular menu item
90 *
91 * Sets the currently pointed-to / highlighted menu item
92 */
93static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id)
94{
8872bc7f
SG
95 if (menu->cur_item_id)
96 update_pointers(menu, menu->cur_item_id, false);
226777f1 97 menu->cur_item_id = item_id;
8872bc7f 98 update_pointers(menu, item_id, true);
226777f1
SG
99}
100
7230fdb3 101static int scene_bbox_union(struct scene *scn, uint id, int inset,
699b0acb
SG
102 struct vidconsole_bbox *bbox)
103{
104 struct scene_obj *obj;
105
106 if (!id)
107 return 0;
108 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
109 if (!obj)
110 return log_msg_ret("obj", -ENOENT);
111 if (bbox->valid) {
7230fdb3 112 bbox->x0 = min(bbox->x0, obj->dim.x - inset);
699b0acb 113 bbox->y0 = min(bbox->y0, obj->dim.y);
7230fdb3 114 bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
699b0acb
SG
115 bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
116 } else {
7230fdb3 117 bbox->x0 = obj->dim.x - inset;
699b0acb 118 bbox->y0 = obj->dim.y;
7230fdb3 119 bbox->x1 = obj->dim.x + obj->dim.w + inset;
699b0acb
SG
120 bbox->y1 = obj->dim.y + obj->dim.h;
121 bbox->valid = true;
122 }
123
124 return 0;
125}
126
127/**
128 * scene_menu_calc_bbox() - Calculate bounding boxes for the menu
129 *
130 * @menu: Menu to process
131 * @bbox: Returns bounding box of menu including prompts
132 * @label_bbox: Returns bounding box of labels
133 */
134static void scene_menu_calc_bbox(struct scene_obj_menu *menu,
135 struct vidconsole_bbox *bbox,
136 struct vidconsole_bbox *label_bbox)
137{
7230fdb3 138 const struct expo_theme *theme = &menu->obj.scene->expo->theme;
699b0acb
SG
139 const struct scene_menitem *item;
140
141 bbox->valid = false;
7230fdb3 142 scene_bbox_union(menu->obj.scene, menu->title_id, 0, bbox);
699b0acb
SG
143
144 label_bbox->valid = false;
145
146 list_for_each_entry(item, &menu->item_head, sibling) {
7230fdb3
SG
147 scene_bbox_union(menu->obj.scene, item->label_id,
148 theme->menu_inset, bbox);
149 scene_bbox_union(menu->obj.scene, item->key_id, 0, bbox);
150 scene_bbox_union(menu->obj.scene, item->desc_id, 0, bbox);
151 scene_bbox_union(menu->obj.scene, item->preview_id, 0, bbox);
699b0acb
SG
152
153 /* Get the bounding box of all labels */
7230fdb3
SG
154 scene_bbox_union(menu->obj.scene, item->label_id,
155 theme->menu_inset, label_bbox);
699b0acb 156 }
7230fdb3
SG
157
158 /*
159 * subtract the final menuitem's gap to keep the insert the same top
160 * and bottom
161 */
162 label_bbox->y1 -= theme->menuitem_gap_y;
699b0acb
SG
163}
164
165int scene_menu_calc_dims(struct scene_obj_menu *menu)
166{
167 struct vidconsole_bbox bbox, label_bbox;
168 const struct scene_menitem *item;
169
170 scene_menu_calc_bbox(menu, &bbox, &label_bbox);
171
172 /* Make all labels the same size */
173 if (label_bbox.valid) {
174 list_for_each_entry(item, &menu->item_head, sibling) {
175 scene_obj_set_size(menu->obj.scene, item->label_id,
176 label_bbox.x1 - label_bbox.x0,
177 label_bbox.y1 - label_bbox.y0);
178 }
179 }
180
181 if (bbox.valid) {
182 menu->obj.dim.w = bbox.x1 - bbox.x0;
183 menu->obj.dim.h = bbox.y1 - bbox.y0;
184 }
185
186 return 0;
187}
188
226777f1
SG
189int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
190{
d3db0216
SG
191 const bool open = menu->obj.flags & SCENEOF_OPEN;
192 struct expo *exp = scn->expo;
193 const bool stack = exp->popup;
7230fdb3 194 const struct expo_theme *theme = &exp->theme;
226777f1 195 struct scene_menitem *item;
8872bc7f 196 uint sel_id;
d3db0216 197 int x, y;
226777f1
SG
198 int ret;
199
d3db0216 200 x = menu->obj.dim.x;
ae45d6cf 201 y = menu->obj.dim.y;
226777f1 202 if (menu->title_id) {
ae45d6cf 203 ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y);
226777f1
SG
204 if (ret < 0)
205 return log_msg_ret("tit", ret);
206
207 ret = scene_obj_get_hw(scn, menu->title_id, NULL);
208 if (ret < 0)
209 return log_msg_ret("hei", ret);
210
d3db0216
SG
211 if (stack)
212 x += 200;
213 else
214 y += ret * 2;
226777f1
SG
215 }
216
217 /*
218 * Currently everything is hard-coded to particular columns so this
219 * won't work on small displays and looks strange if the font size is
220 * small. This can be updated once text measuring is supported in
221 * vidconsole
222 */
8872bc7f 223 sel_id = menu->cur_item_id;
226777f1 224 list_for_each_entry(item, &menu->item_head, sibling) {
d3db0216 225 bool selected;
226777f1
SG
226 int height;
227
d3db0216 228 ret = scene_obj_get_hw(scn, item->label_id, NULL);
226777f1
SG
229 if (ret < 0)
230 return log_msg_ret("get", ret);
231 height = ret;
232
233 if (item->flags & SCENEMIF_GAP_BEFORE)
234 y += height;
235
236 /* select an item if not done already */
8872bc7f
SG
237 if (!sel_id)
238 sel_id = item->id;
226777f1 239
d3db0216
SG
240 selected = sel_id == item->id;
241
226777f1
SG
242 /*
243 * Put the label on the left, then leave a space for the
244 * pointer, then the key and the description
245 */
7230fdb3
SG
246 ret = scene_obj_set_pos(scn, item->label_id,
247 x + theme->menu_inset, y);
226777f1 248 if (ret < 0)
d3db0216
SG
249 return log_msg_ret("nam", ret);
250 scene_obj_set_hide(scn, item->label_id,
251 stack && !open && !selected);
226777f1 252
d3db0216
SG
253 if (item->key_id) {
254 ret = scene_obj_set_pos(scn, item->key_id, x + 230, y);
255 if (ret < 0)
256 return log_msg_ret("key", ret);
257 }
226777f1 258
d3db0216
SG
259 if (item->desc_id) {
260 ret = scene_obj_set_pos(scn, item->desc_id, x + 280, y);
261 if (ret < 0)
262 return log_msg_ret("des", ret);
263 }
226777f1
SG
264
265 if (item->preview_id) {
266 bool hide;
267
268 /*
269 * put all previews on top of each other, on the right
270 * size of the display
271 */
272 ret = scene_obj_set_pos(scn, item->preview_id, -4, y);
273 if (ret < 0)
274 return log_msg_ret("prev", ret);
275
276 hide = menu->cur_item_id != item->id;
277 ret = scene_obj_set_hide(scn, item->preview_id, hide);
278 if (ret < 0)
279 return log_msg_ret("hid", ret);
280 }
281
d3db0216 282 if (!stack || open)
7230fdb3 283 y += height + theme->menuitem_gap_y;
226777f1
SG
284 }
285
8872bc7f
SG
286 if (sel_id)
287 menu_point_to_item(menu, sel_id);
226777f1
SG
288
289 return 0;
290}
291
292int scene_menu(struct scene *scn, const char *name, uint id,
293 struct scene_obj_menu **menup)
294{
295 struct scene_obj_menu *menu;
296 int ret;
297
298 ret = scene_obj_add(scn, name, id, SCENEOBJT_MENU,
299 sizeof(struct scene_obj_menu),
300 (struct scene_obj **)&menu);
301 if (ret < 0)
302 return log_msg_ret("obj", -ENOMEM);
303
304 if (menup)
305 *menup = menu;
306 INIT_LIST_HEAD(&menu->item_head);
307
226777f1
SG
308 return menu->obj.id;
309}
310
311static struct scene_menitem *scene_menu_find_key(struct scene *scn,
312 struct scene_obj_menu *menu,
313 int key)
314{
315 struct scene_menitem *item;
316
317 list_for_each_entry(item, &menu->item_head, sibling) {
318 if (item->key_id) {
319 struct scene_obj_txt *txt;
320 const char *str;
321
322 txt = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
323 if (txt) {
324 str = expo_get_str(scn->expo, txt->str_id);
325 if (str && *str == key)
326 return item;
327 }
328 }
329 }
330
331 return NULL;
332}
333
334int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
335 struct expo_action *event)
336{
4e64beeb 337 const bool open = menu->obj.flags & SCENEOF_OPEN;
226777f1
SG
338 struct scene_menitem *item, *cur, *key_item;
339
340 cur = NULL;
341 key_item = NULL;
342
343 if (!list_empty(&menu->item_head)) {
344 list_for_each_entry(item, &menu->item_head, sibling) {
345 /* select an item if not done already */
346 if (menu->cur_item_id == item->id) {
347 cur = item;
348 break;
349 }
350 }
351 }
352
353 if (!cur)
354 return -ENOTTY;
355
356 switch (key) {
357 case BKEY_UP:
358 if (item != list_first_entry(&menu->item_head,
359 struct scene_menitem, sibling)) {
360 item = list_entry(item->sibling.prev,
361 struct scene_menitem, sibling);
3f33b9c7 362 event->type = EXPOACT_POINT_ITEM;
226777f1
SG
363 event->select.id = item->id;
364 log_debug("up to item %d\n", event->select.id);
365 }
366 break;
367 case BKEY_DOWN:
368 if (!list_is_last(&item->sibling, &menu->item_head)) {
369 item = list_entry(item->sibling.next,
370 struct scene_menitem, sibling);
3f33b9c7 371 event->type = EXPOACT_POINT_ITEM;
226777f1
SG
372 event->select.id = item->id;
373 log_debug("down to item %d\n", event->select.id);
374 }
375 break;
376 case BKEY_SELECT:
377 event->type = EXPOACT_SELECT;
378 event->select.id = item->id;
379 log_debug("select item %d\n", event->select.id);
380 break;
381 case BKEY_QUIT:
4e64beeb
SG
382 if (scn->expo->popup && open) {
383 event->type = EXPOACT_CLOSE;
384 event->select.id = menu->obj.id;
385 } else {
386 event->type = EXPOACT_QUIT;
387 log_debug("menu quit\n");
388 }
226777f1
SG
389 break;
390 case '0'...'9':
391 key_item = scene_menu_find_key(scn, menu, key);
392 if (key_item) {
393 event->type = EXPOACT_SELECT;
394 event->select.id = key_item->id;
395 }
396 break;
397 }
398
399 menu_point_to_item(menu, item->id);
400
401 return 0;
402}
403
404int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
405 uint key_id, uint label_id, uint desc_id, uint preview_id,
406 uint flags, struct scene_menitem **itemp)
407{
408 struct scene_obj_menu *menu;
409 struct scene_menitem *item;
226777f1
SG
410
411 menu = scene_obj_find(scn, menu_id, SCENEOBJT_MENU);
412 if (!menu)
413 return log_msg_ret("find", -ENOENT);
414
415 /* Check that the text ID is valid */
d3db0216 416 if (!scene_obj_find(scn, label_id, SCENEOBJT_TEXT))
226777f1
SG
417 return log_msg_ret("txt", -EINVAL);
418
419 item = calloc(1, sizeof(struct scene_obj_menu));
420 if (!item)
421 return log_msg_ret("item", -ENOMEM);
422 item->name = strdup(name);
423 if (!item->name) {
424 free(item);
425 return log_msg_ret("name", -ENOMEM);
426 }
427
428 item->id = resolve_id(scn->expo, id);
429 item->key_id = key_id;
430 item->label_id = label_id;
431 item->desc_id = desc_id;
432 item->preview_id = preview_id;
433 item->flags = flags;
434 list_add_tail(&item->sibling, &menu->item_head);
435
226777f1
SG
436 if (itemp)
437 *itemp = item;
438
439 return item->id;
440}
441
442int scene_menu_set_title(struct scene *scn, uint id, uint title_id)
443{
444 struct scene_obj_menu *menu;
445 struct scene_obj_txt *txt;
446
447 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
448 if (!menu)
449 return log_msg_ret("menu", -ENOENT);
450
451 /* Check that the ID is valid */
452 if (title_id) {
453 txt = scene_obj_find(scn, title_id, SCENEOBJT_TEXT);
454 if (!txt)
455 return log_msg_ret("txt", -EINVAL);
456 }
457
458 menu->title_id = title_id;
459
460 return 0;
461}
462
463int scene_menu_set_pointer(struct scene *scn, uint id, uint pointer_id)
464{
465 struct scene_obj_menu *menu;
466 struct scene_obj *obj;
467
468 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
469 if (!menu)
470 return log_msg_ret("menu", -ENOENT);
471
472 /* Check that the ID is valid */
473 if (pointer_id) {
474 obj = scene_obj_find(scn, pointer_id, SCENEOBJT_NONE);
475 if (!obj)
476 return log_msg_ret("obj", -EINVAL);
477 }
478
479 menu->pointer_id = pointer_id;
480
481 return 0;
482}
483
484int scene_menu_display(struct scene_obj_menu *menu)
485{
486 struct scene *scn = menu->obj.scene;
487 struct scene_obj_txt *pointer;
488 struct expo *exp = scn->expo;
489 struct scene_menitem *item;
490 const char *pstr;
491
492 printf("U-Boot : Boot Menu\n\n");
493 if (menu->title_id) {
494 struct scene_obj_txt *txt;
495 const char *str;
496
497 txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_TEXT);
498 if (!txt)
499 return log_msg_ret("txt", -EINVAL);
500
501 str = expo_get_str(exp, txt->str_id);
502 printf("%s\n\n", str);
503 }
504
505 if (list_empty(&menu->item_head))
506 return 0;
507
508 pointer = scene_obj_find(scn, menu->pointer_id, SCENEOBJT_TEXT);
509 pstr = expo_get_str(scn->expo, pointer->str_id);
510
511 list_for_each_entry(item, &menu->item_head, sibling) {
512 struct scene_obj_txt *key = NULL, *label = NULL;
513 struct scene_obj_txt *desc = NULL;
514 const char *kstr = NULL, *lstr = NULL, *dstr = NULL;
515
516 key = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
517 if (key)
518 kstr = expo_get_str(exp, key->str_id);
519
520 label = scene_obj_find(scn, item->label_id, SCENEOBJT_TEXT);
521 if (label)
522 lstr = expo_get_str(exp, label->str_id);
523
524 desc = scene_obj_find(scn, item->desc_id, SCENEOBJT_TEXT);
525 if (desc)
526 dstr = expo_get_str(exp, desc->str_id);
527
528 printf("%3s %3s %-10s %s\n",
529 pointer && menu->cur_item_id == item->id ? pstr : "",
530 kstr, lstr, dstr);
531 }
532
533 return -ENOTSUPP;
534}
756c9559
SG
535
536void scene_menu_render(struct scene_obj_menu *menu)
537{
538 struct expo *exp = menu->obj.scene->expo;
539 const struct expo_theme *theme = &exp->theme;
540 struct vidconsole_bbox bbox, label_bbox;
541 struct udevice *dev = exp->display;
542 struct video_priv *vid_priv;
543 struct udevice *cons = exp->cons;
544 struct vidconsole_colour old;
545 enum colour_idx fore, back;
546
547 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
548 fore = VID_BLACK;
549 back = VID_WHITE;
550 } else {
551 fore = VID_LIGHT_GRAY;
552 back = VID_BLACK;
553 }
554
555 scene_menu_calc_bbox(menu, &bbox, &label_bbox);
556 vidconsole_push_colour(cons, fore, back, &old);
557 vid_priv = dev_get_uclass_priv(dev);
558 video_fill_part(dev, label_bbox.x0 - theme->menu_inset,
559 label_bbox.y0 - theme->menu_inset,
560 label_bbox.x1, label_bbox.y1 + theme->menu_inset,
561 vid_priv->colour_fg);
562 vidconsole_pop_colour(cons, &old);
563}
4c87e073
SG
564
565int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu)
566{
567 struct scene_menitem *item;
568
569 scene_render_deps(scn, menu->title_id);
570 scene_render_deps(scn, menu->cur_item_id);
571 scene_render_deps(scn, menu->pointer_id);
572
573 list_for_each_entry(item, &menu->item_head, sibling) {
574 scene_render_deps(scn, item->key_id);
575 scene_render_deps(scn, item->label_id);
576 scene_render_deps(scn, item->desc_id);
577 }
578
579 return 0;
580}
This page took 0.098654 seconds and 4 git commands to generate.