doc: usage: load: document part as hexadecimal
[platform/kernel/u-boot.git] / boot / scene.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Implementation of a scene, a collection of text/image/menu items in an expo
4  *
5  * Copyright 2022 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
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 int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
23 {
24         struct scene *scn;
25
26         scn = calloc(1, sizeof(struct scene));
27         if (!scn)
28                 return log_msg_ret("expo", -ENOMEM);
29         scn->name = strdup(name);
30         if (!scn->name) {
31                 free(scn);
32                 return log_msg_ret("name", -ENOMEM);
33         }
34
35         INIT_LIST_HEAD(&scn->obj_head);
36         scn->id = resolve_id(exp, id);
37         scn->expo = exp;
38         list_add_tail(&scn->sibling, &exp->scene_head);
39
40         *scnp = scn;
41
42         return scn->id;
43 }
44
45 void scene_obj_destroy(struct scene_obj *obj)
46 {
47         if (obj->type == SCENEOBJT_MENU)
48                 scene_menu_destroy((struct scene_obj_menu *)obj);
49         free(obj->name);
50         free(obj);
51 }
52
53 void scene_destroy(struct scene *scn)
54 {
55         struct scene_obj *obj, *next;
56
57         list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
58                 scene_obj_destroy(obj);
59
60         free(scn->name);
61         free(scn);
62 }
63
64 int scene_title_set(struct scene *scn, uint id)
65 {
66         scn->title_id = id;
67
68         return 0;
69 }
70
71 int scene_obj_count(struct scene *scn)
72 {
73         struct scene_obj *obj;
74         int count = 0;
75
76         list_for_each_entry(obj, &scn->obj_head, sibling)
77                 count++;
78
79         return count;
80 }
81
82 void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type)
83 {
84         struct scene_obj *obj;
85
86         list_for_each_entry(obj, &scn->obj_head, sibling) {
87                 if (obj->id == id &&
88                     (type == SCENEOBJT_NONE || obj->type == type))
89                         return obj;
90         }
91
92         return NULL;
93 }
94
95 void *scene_obj_find_by_name(struct scene *scn, const char *name)
96 {
97         struct scene_obj *obj;
98
99         list_for_each_entry(obj, &scn->obj_head, sibling) {
100                 if (!strcmp(name, obj->name))
101                         return obj;
102         }
103
104         return NULL;
105 }
106
107 int scene_obj_add(struct scene *scn, const char *name, uint id,
108                   enum scene_obj_t type, uint size, struct scene_obj **objp)
109 {
110         struct scene_obj *obj;
111
112         obj = calloc(1, size);
113         if (!obj)
114                 return log_msg_ret("obj", -ENOMEM);
115         obj->name = strdup(name);
116         if (!obj->name) {
117                 free(obj);
118                 return log_msg_ret("name", -ENOMEM);
119         }
120
121         obj->id = resolve_id(scn->expo, id);
122         obj->scene = scn;
123         obj->type = type;
124         list_add_tail(&obj->sibling, &scn->obj_head);
125         *objp = obj;
126
127         return obj->id;
128 }
129
130 int scene_img(struct scene *scn, const char *name, uint id, char *data,
131               struct scene_obj_img **imgp)
132 {
133         struct scene_obj_img *img;
134         int ret;
135
136         ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
137                             sizeof(struct scene_obj_img),
138                             (struct scene_obj **)&img);
139         if (ret < 0)
140                 return log_msg_ret("obj", -ENOMEM);
141
142         img->data = data;
143
144         if (imgp)
145                 *imgp = img;
146
147         return img->obj.id;
148 }
149
150 int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
151               struct scene_obj_txt **txtp)
152 {
153         struct scene_obj_txt *txt;
154         int ret;
155
156         ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
157                             sizeof(struct scene_obj_txt),
158                             (struct scene_obj **)&txt);
159         if (ret < 0)
160                 return log_msg_ret("obj", -ENOMEM);
161
162         txt->str_id = str_id;
163
164         if (txtp)
165                 *txtp = txt;
166
167         return txt->obj.id;
168 }
169
170 int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
171                   const char *str, struct scene_obj_txt **txtp)
172 {
173         struct scene_obj_txt *txt;
174         int ret;
175
176         ret = expo_str(scn->expo, name, str_id, str);
177         if (ret < 0)
178                 return log_msg_ret("str", ret);
179         else if (ret != str_id)
180                 return log_msg_ret("id", -EEXIST);
181
182         ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
183                             sizeof(struct scene_obj_txt),
184                             (struct scene_obj **)&txt);
185         if (ret < 0)
186                 return log_msg_ret("obj", -ENOMEM);
187
188         txt->str_id = str_id;
189
190         if (txtp)
191                 *txtp = txt;
192
193         return txt->obj.id;
194 }
195
196 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
197                        uint font_size)
198 {
199         struct scene_obj_txt *txt;
200
201         txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
202         if (!txt)
203                 return log_msg_ret("find", -ENOENT);
204         txt->font_name = font_name;
205         txt->font_size = font_size;
206
207         return 0;
208 }
209
210 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
211 {
212         struct scene_obj *obj;
213
214         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
215         if (!obj)
216                 return log_msg_ret("find", -ENOENT);
217         obj->dim.x = x;
218         obj->dim.y = y;
219
220         return 0;
221 }
222
223 int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
224 {
225         struct scene_obj *obj;
226
227         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
228         if (!obj)
229                 return log_msg_ret("find", -ENOENT);
230         obj->dim.w = w;
231         obj->dim.h = h;
232
233         return 0;
234 }
235
236 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
237 {
238         int ret;
239
240         ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
241                                     hide ? SCENEOF_HIDE : 0);
242         if (ret)
243                 return log_msg_ret("flg", ret);
244
245         return 0;
246 }
247
248 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
249 {
250         struct scene_obj *obj;
251
252         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
253         if (!obj)
254                 return log_msg_ret("find", -ENOENT);
255         obj->flags &= ~clr;
256         obj->flags |= set;
257
258         return 0;
259 }
260
261 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
262 {
263         struct scene_obj *obj;
264
265         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
266         if (!obj)
267                 return log_msg_ret("find", -ENOENT);
268
269         switch (obj->type) {
270         case SCENEOBJT_NONE:
271         case SCENEOBJT_MENU:
272                 break;
273         case SCENEOBJT_IMAGE: {
274                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
275                 ulong width, height;
276                 uint bpix;
277
278                 video_bmp_get_info(img->data, &width, &height, &bpix);
279                 if (widthp)
280                         *widthp = width;
281                 return height;
282         }
283         case SCENEOBJT_TEXT: {
284                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
285                 struct expo *exp = scn->expo;
286                 struct vidconsole_bbox bbox;
287                 const char *str;
288                 int len, ret;
289
290                 str = expo_get_str(exp, txt->str_id);
291                 if (!str)
292                         return log_msg_ret("str", -ENOENT);
293                 len = strlen(str);
294
295                 /* if there is no console, make it up */
296                 if (!exp->cons) {
297                         if (widthp)
298                                 *widthp = 8 * len;
299                         return 16;
300                 }
301
302                 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
303                                          txt->font_size, str, &bbox);
304                 if (ret)
305                         return log_msg_ret("mea", ret);
306                 if (widthp)
307                         *widthp = bbox.x1;
308
309                 return bbox.y1;
310         }
311         }
312
313         return 0;
314 }
315
316 /**
317  * scene_obj_render() - Render an object
318  *
319  */
320 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
321 {
322         struct scene *scn = obj->scene;
323         struct expo *exp = scn->expo;
324         const struct expo_theme *theme = &exp->theme;
325         struct udevice *dev = exp->display;
326         struct udevice *cons = text_mode ? NULL : exp->cons;
327         int x, y, ret;
328
329         x = obj->dim.x;
330         y = obj->dim.y;
331
332         switch (obj->type) {
333         case SCENEOBJT_NONE:
334                 break;
335         case SCENEOBJT_IMAGE: {
336                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
337
338                 if (!cons)
339                         return -ENOTSUPP;
340                 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
341                                         true);
342                 if (ret < 0)
343                         return log_msg_ret("img", ret);
344                 break;
345         }
346         case SCENEOBJT_TEXT: {
347                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
348                 const char *str;
349
350                 if (!cons)
351                         return -ENOTSUPP;
352
353                 if (txt->font_name || txt->font_size) {
354                         ret = vidconsole_select_font(cons,
355                                                      txt->font_name,
356                                                      txt->font_size);
357                 } else {
358                         ret = vidconsole_select_font(cons, NULL, 0);
359                 }
360                 if (ret && ret != -ENOSYS)
361                         return log_msg_ret("font", ret);
362                 str = expo_get_str(exp, txt->str_id);
363                 if (str) {
364                         struct video_priv *vid_priv;
365                         struct vidconsole_colour old;
366                         enum colour_idx fore, back;
367
368                         if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
369                                 fore = VID_BLACK;
370                                 back = VID_WHITE;
371                         } else {
372                                 fore = VID_LIGHT_GRAY;
373                                 back = VID_BLACK;
374                         }
375
376                         vid_priv = dev_get_uclass_priv(dev);
377                         if (obj->flags & SCENEOF_POINT) {
378                                 vidconsole_push_colour(cons, fore, back, &old);
379                                 video_fill_part(dev, x - theme->menu_inset, y,
380                                                 x + obj->dim.w,
381                                                 y + obj->dim.h,
382                                                 vid_priv->colour_bg);
383                         }
384                         vidconsole_set_cursor_pos(cons, x, y);
385                         vidconsole_put_string(cons, str);
386                         if (obj->flags & SCENEOF_POINT)
387                                 vidconsole_pop_colour(cons, &old);
388                 }
389                 break;
390         }
391         case SCENEOBJT_MENU: {
392                 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
393
394                 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
395                         if (!cons)
396                                 return -ENOTSUPP;
397
398                         /* draw a background behind the menu items */
399                         scene_menu_render(menu);
400                 }
401                 /*
402                  * With a vidconsole, the text and item pointer are rendered as
403                  * normal objects so we don't need to do anything here. The menu
404                  * simply controls where they are positioned.
405                  */
406                 if (cons)
407                         return -ENOTSUPP;
408
409                 ret = scene_menu_display(menu);
410                 if (ret < 0)
411                         return log_msg_ret("img", ret);
412
413                 break;
414         }
415         }
416
417         return 0;
418 }
419
420 int scene_arrange(struct scene *scn)
421 {
422         struct scene_obj *obj;
423         int ret;
424
425         list_for_each_entry(obj, &scn->obj_head, sibling) {
426                 if (obj->type == SCENEOBJT_MENU) {
427                         struct scene_obj_menu *menu;
428
429                         menu = (struct scene_obj_menu *)obj,
430                         ret = scene_menu_arrange(scn, menu);
431                         if (ret)
432                                 return log_msg_ret("arr", ret);
433                 }
434         }
435
436         return 0;
437 }
438
439 int scene_render_deps(struct scene *scn, uint id)
440 {
441         struct scene_obj *obj;
442         int ret;
443
444         if (!id)
445                 return 0;
446         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
447         if (!obj)
448                 return log_msg_ret("obj", -ENOENT);
449
450         if (!(obj->flags & SCENEOF_HIDE)) {
451                 ret = scene_obj_render(obj, false);
452                 if (ret && ret != -ENOTSUPP)
453                         return log_msg_ret("ren", ret);
454
455                 if (obj->type == SCENEOBJT_MENU)
456                         scene_menu_render_deps(scn,
457                                                (struct scene_obj_menu *)obj);
458         }
459
460         return 0;
461 }
462
463 int scene_render(struct scene *scn)
464 {
465         struct expo *exp = scn->expo;
466         struct scene_obj *obj;
467         int ret;
468
469         list_for_each_entry(obj, &scn->obj_head, sibling) {
470                 if (!(obj->flags & SCENEOF_HIDE)) {
471                         ret = scene_obj_render(obj, exp->text_mode);
472                         if (ret && ret != -ENOTSUPP)
473                                 return log_msg_ret("ren", ret);
474                 }
475         }
476
477         /* render any highlighted object on top of the others */
478         if (scn->highlight_id && !exp->text_mode) {
479                 ret = scene_render_deps(scn, scn->highlight_id);
480                 if (ret && ret != -ENOTSUPP)
481                         return log_msg_ret("dep", ret);
482         }
483
484         return 0;
485 }
486
487 /**
488  * send_key_obj() - Handle a keypress for moving between objects
489  *
490  * @scn: Scene to receive the key
491  * @key: Key to send (KEYCODE_UP)
492  * @event: Returns resulting event from this keypress
493  * Returns: 0 if OK, -ve on error
494  */
495 static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
496                          struct expo_action *event)
497 {
498         switch (key) {
499         case BKEY_UP:
500                 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
501                                                sibling)) {
502                         obj = list_entry(obj->sibling.prev,
503                                          struct scene_obj, sibling);
504                         if (obj->type == SCENEOBJT_MENU) {
505                                 event->type = EXPOACT_POINT_OBJ;
506                                 event->select.id = obj->id;
507                                 log_debug("up to obj %d\n", event->select.id);
508                                 break;
509                         }
510                 }
511                 break;
512         case BKEY_DOWN:
513                 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
514                         obj = list_entry(obj->sibling.next, struct scene_obj,
515                                          sibling);
516                         if (obj->type == SCENEOBJT_MENU) {
517                                 event->type = EXPOACT_POINT_OBJ;
518                                 event->select.id = obj->id;
519                                 log_debug("down to obj %d\n", event->select.id);
520                                 break;
521                         }
522                 }
523                 break;
524         case BKEY_SELECT:
525                 if (obj->type == SCENEOBJT_MENU) {
526                         event->type = EXPOACT_OPEN;
527                         event->select.id = obj->id;
528                         log_debug("open obj %d\n", event->select.id);
529                 }
530                 break;
531         case BKEY_QUIT:
532                 event->type = EXPOACT_QUIT;
533                 log_debug("obj quit\n");
534                 break;
535         }
536 }
537
538 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
539 {
540         struct scene_obj_menu *menu;
541         struct scene_obj *obj;
542         int ret;
543
544         event->type = EXPOACT_NONE;
545
546         /*
547          * In 'popup' mode, arrow keys move betwen objects, unless a menu is
548          * opened
549          */
550         if (scn->expo->popup) {
551                 obj = NULL;
552                 if (scn->highlight_id) {
553                         obj = scene_obj_find(scn, scn->highlight_id,
554                                              SCENEOBJT_NONE);
555                 }
556                 if (!obj)
557                         return 0;
558
559                 if (!(obj->flags & SCENEOF_OPEN)) {
560                         send_key_obj(scn, obj, key, event);
561                         return 0;
562                 }
563
564                 menu = (struct scene_obj_menu *)obj,
565                 ret = scene_menu_send_key(scn, menu, key, event);
566                 if (ret)
567                         return log_msg_ret("key", ret);
568                 return 0;
569         }
570
571         list_for_each_entry(obj, &scn->obj_head, sibling) {
572                 if (obj->type == SCENEOBJT_MENU) {
573                         struct scene_obj_menu *menu;
574
575                         menu = (struct scene_obj_menu *)obj,
576                         ret = scene_menu_send_key(scn, menu, key, event);
577                         if (ret)
578                                 return log_msg_ret("key", ret);
579                         break;
580                 }
581         }
582
583         return 0;
584 }
585
586 int scene_calc_dims(struct scene *scn, bool do_menus)
587 {
588         struct scene_obj *obj;
589         int ret;
590
591         list_for_each_entry(obj, &scn->obj_head, sibling) {
592                 switch (obj->type) {
593                 case SCENEOBJT_NONE:
594                 case SCENEOBJT_TEXT:
595                 case SCENEOBJT_IMAGE: {
596                         int width;
597
598                         if (!do_menus) {
599                                 ret = scene_obj_get_hw(scn, obj->id, &width);
600                                 if (ret < 0)
601                                         return log_msg_ret("get", ret);
602                                 obj->dim.w = width;
603                                 obj->dim.h = ret;
604                         }
605                         break;
606                 }
607                 case SCENEOBJT_MENU: {
608                         struct scene_obj_menu *menu;
609
610                         if (do_menus) {
611                                 menu = (struct scene_obj_menu *)obj;
612
613                                 ret = scene_menu_calc_dims(menu);
614                                 if (ret)
615                                         return log_msg_ret("men", ret);
616                         }
617                         break;
618                 }
619                 }
620         }
621
622         return 0;
623 }
624
625 int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
626 {
627         struct scene_obj *obj;
628         int ret;
629
630         /* Avoid error-checking optional items */
631         scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
632
633         list_for_each_entry(obj, &scn->obj_head, sibling) {
634                 switch (obj->type) {
635                 case SCENEOBJT_NONE:
636                 case SCENEOBJT_IMAGE:
637                 case SCENEOBJT_MENU:
638                         break;
639                 case SCENEOBJT_TEXT:
640                         scene_txt_set_font(scn, obj->id, NULL,
641                                            theme->font_size);
642                         break;
643                 }
644         }
645
646         ret = scene_arrange(scn);
647         if (ret)
648                 return log_msg_ret("arr", ret);
649
650         return 0;
651 }
652
653 void scene_set_highlight_id(struct scene *scn, uint id)
654 {
655         scn->highlight_id = id;
656 }
657
658 void scene_highlight_first(struct scene *scn)
659 {
660         struct scene_obj *obj;
661
662         list_for_each_entry(obj, &scn->obj_head, sibling) {
663                 switch (obj->type) {
664                 case SCENEOBJT_MENU:
665                         scene_set_highlight_id(scn, obj->id);
666                         return;
667                 default:
668                         break;
669                 }
670         }
671 }
672
673 int scene_set_open(struct scene *scn, uint id, bool open)
674 {
675         int ret;
676
677         ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
678                                     open ? SCENEOF_OPEN : 0);
679         if (ret)
680                 return log_msg_ret("flg", ret);
681
682         return 0;
683 }