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