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