configs: starfive: add starfive_visionfive2_defconfig
[platform/kernel/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 <sjg@chromium.org>
7  */
8
9 #define LOG_CATEGORY    LOGC_BOOT
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 /**
37  * menu_point_to_item() - Point to a particular menu item
38  *
39  * Sets the currently pointed-to / highlighted menu item
40  */
41 static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id)
42 {
43         menu->cur_item_id = item_id;
44 }
45
46 int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
47 {
48         struct scene_menitem *item;
49         int y, cur_y;
50         int ret;
51
52         y = menu->obj.y;
53         if (menu->title_id) {
54                 ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.x, y);
55                 if (ret < 0)
56                         return log_msg_ret("tit", ret);
57
58                 ret = scene_obj_get_hw(scn, menu->title_id, NULL);
59                 if (ret < 0)
60                         return log_msg_ret("hei", ret);
61
62                 y += ret * 2;
63         }
64
65         /*
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
69          * vidconsole
70          */
71         cur_y = -1;
72         list_for_each_entry(item, &menu->item_head, sibling) {
73                 int height;
74
75                 ret = scene_obj_get_hw(scn, item->desc_id, NULL);
76                 if (ret < 0)
77                         return log_msg_ret("get", ret);
78                 height = ret;
79
80                 if (item->flags & SCENEMIF_GAP_BEFORE)
81                         y += height;
82
83                 /* select an item if not done already */
84                 if (!menu->cur_item_id)
85                         menu_point_to_item(menu, item->id);
86
87                 /*
88                  * Put the label on the left, then leave a space for the
89                  * pointer, then the key and the description
90                  */
91                 if (item->label_id) {
92                         ret = scene_obj_set_pos(scn, item->label_id, menu->obj.x,
93                                                 y);
94                         if (ret < 0)
95                                 return log_msg_ret("nam", ret);
96                 }
97
98                 ret = scene_obj_set_pos(scn, item->key_id, menu->obj.x + 230,
99                                         y);
100                 if (ret < 0)
101                         return log_msg_ret("key", ret);
102
103                 ret = scene_obj_set_pos(scn, item->desc_id, menu->obj.x + 280,
104                                         y);
105                 if (ret < 0)
106                         return log_msg_ret("des", ret);
107
108                 if (menu->cur_item_id == item->id)
109                         cur_y = y;
110
111                 if (item->preview_id) {
112                         bool hide;
113
114                         /*
115                          * put all previews on top of each other, on the right
116                          * size of the display
117                          */
118                         ret = scene_obj_set_pos(scn, item->preview_id, -4, y);
119                         if (ret < 0)
120                                 return log_msg_ret("prev", ret);
121
122                         hide = menu->cur_item_id != item->id;
123                         ret = scene_obj_set_hide(scn, item->preview_id, hide);
124                         if (ret < 0)
125                                 return log_msg_ret("hid", ret);
126                 }
127
128                 y += height;
129         }
130
131         if (menu->pointer_id && cur_y != -1) {
132                 /*
133                  * put the pointer to the right of and level with the item it
134                  * points to
135                  */
136                 ret = scene_obj_set_pos(scn, menu->pointer_id,
137                                         menu->obj.x + 200, cur_y);
138                 if (ret < 0)
139                         return log_msg_ret("ptr", ret);
140         }
141
142         return 0;
143 }
144
145 int scene_menu(struct scene *scn, const char *name, uint id,
146                struct scene_obj_menu **menup)
147 {
148         struct scene_obj_menu *menu;
149         int ret;
150
151         ret = scene_obj_add(scn, name, id, SCENEOBJT_MENU,
152                             sizeof(struct scene_obj_menu),
153                             (struct scene_obj **)&menu);
154         if (ret < 0)
155                 return log_msg_ret("obj", -ENOMEM);
156
157         if (menup)
158                 *menup = menu;
159         INIT_LIST_HEAD(&menu->item_head);
160
161         ret = scene_menu_arrange(scn, menu);
162         if (ret)
163                 return log_msg_ret("pos", ret);
164
165         return menu->obj.id;
166 }
167
168 static struct scene_menitem *scene_menu_find_key(struct scene *scn,
169                                                   struct scene_obj_menu *menu,
170                                                   int key)
171 {
172         struct scene_menitem *item;
173
174         list_for_each_entry(item, &menu->item_head, sibling) {
175                 if (item->key_id) {
176                         struct scene_obj_txt *txt;
177                         const char *str;
178
179                         txt = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
180                         if (txt) {
181                                 str = expo_get_str(scn->expo, txt->str_id);
182                                 if (str && *str == key)
183                                         return item;
184                         }
185                 }
186         }
187
188         return NULL;
189 }
190
191 int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
192                         struct expo_action *event)
193 {
194         struct scene_menitem *item, *cur, *key_item;
195
196         cur = NULL;
197         key_item = NULL;
198
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) {
203                                 cur = item;
204                                 break;
205                         }
206                 }
207         }
208
209         if (!cur)
210                 return -ENOTTY;
211
212         switch (key) {
213         case BKEY_UP:
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);
221                 }
222                 break;
223         case BKEY_DOWN:
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);
230                 }
231                 break;
232         case BKEY_SELECT:
233                 event->type = EXPOACT_SELECT;
234                 event->select.id = item->id;
235                 log_debug("select item %d\n", event->select.id);
236                 break;
237         case BKEY_QUIT:
238                 event->type = EXPOACT_QUIT;
239                 log_debug("quit\n");
240                 break;
241         case '0'...'9':
242                 key_item = scene_menu_find_key(scn, menu, key);
243                 if (key_item) {
244                         event->type = EXPOACT_SELECT;
245                         event->select.id = key_item->id;
246                 }
247                 break;
248         }
249
250         menu_point_to_item(menu, item->id);
251
252         return 0;
253 }
254
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)
258 {
259         struct scene_obj_menu *menu;
260         struct scene_menitem *item;
261         int ret;
262
263         menu = scene_obj_find(scn, menu_id, SCENEOBJT_MENU);
264         if (!menu)
265                 return log_msg_ret("find", -ENOENT);
266
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);
270
271         item = calloc(1, sizeof(struct scene_obj_menu));
272         if (!item)
273                 return log_msg_ret("item", -ENOMEM);
274         item->name = strdup(name);
275         if (!item->name) {
276                 free(item);
277                 return log_msg_ret("name", -ENOMEM);
278         }
279
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;
285         item->flags = flags;
286         list_add_tail(&item->sibling, &menu->item_head);
287
288         ret = scene_menu_arrange(scn, menu);
289         if (ret)
290                 return log_msg_ret("pos", ret);
291
292         if (itemp)
293                 *itemp = item;
294
295         return item->id;
296 }
297
298 int scene_menu_set_title(struct scene *scn, uint id, uint title_id)
299 {
300         struct scene_obj_menu *menu;
301         struct scene_obj_txt *txt;
302
303         menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
304         if (!menu)
305                 return log_msg_ret("menu", -ENOENT);
306
307         /* Check that the ID is valid */
308         if (title_id) {
309                 txt = scene_obj_find(scn, title_id, SCENEOBJT_TEXT);
310                 if (!txt)
311                         return log_msg_ret("txt", -EINVAL);
312         }
313
314         menu->title_id = title_id;
315
316         return 0;
317 }
318
319 int scene_menu_set_pointer(struct scene *scn, uint id, uint pointer_id)
320 {
321         struct scene_obj_menu *menu;
322         struct scene_obj *obj;
323
324         menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
325         if (!menu)
326                 return log_msg_ret("menu", -ENOENT);
327
328         /* Check that the ID is valid */
329         if (pointer_id) {
330                 obj = scene_obj_find(scn, pointer_id, SCENEOBJT_NONE);
331                 if (!obj)
332                         return log_msg_ret("obj", -EINVAL);
333         }
334
335         menu->pointer_id = pointer_id;
336
337         return 0;
338 }
339
340 int scene_menu_display(struct scene_obj_menu *menu)
341 {
342         struct scene *scn = menu->obj.scene;
343         struct scene_obj_txt *pointer;
344         struct expo *exp = scn->expo;
345         struct scene_menitem *item;
346         const char *pstr;
347
348         printf("U-Boot    :    Boot Menu\n\n");
349         if (menu->title_id) {
350                 struct scene_obj_txt *txt;
351                 const char *str;
352
353                 txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_TEXT);
354                 if (!txt)
355                         return log_msg_ret("txt", -EINVAL);
356
357                 str = expo_get_str(exp, txt->str_id);
358                 printf("%s\n\n", str);
359         }
360
361         if (list_empty(&menu->item_head))
362                 return 0;
363
364         pointer = scene_obj_find(scn, menu->pointer_id, SCENEOBJT_TEXT);
365         pstr = expo_get_str(scn->expo, pointer->str_id);
366
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;
371
372                 key = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
373                 if (key)
374                         kstr = expo_get_str(exp, key->str_id);
375
376                 label = scene_obj_find(scn, item->label_id, SCENEOBJT_TEXT);
377                 if (label)
378                         lstr = expo_get_str(exp, label->str_id);
379
380                 desc = scene_obj_find(scn, item->desc_id, SCENEOBJT_TEXT);
381                 if (desc)
382                         dstr = expo_get_str(exp, desc->str_id);
383
384                 printf("%3s  %3s  %-10s  %s\n",
385                        pointer && menu->cur_item_id == item->id ? pstr : "",
386                        kstr, lstr, dstr);
387         }
388
389         return -ENOTSUPP;
390 }