1 // SPDX-License-Identifier: GPL-2.0+
3 * Implementation of a scene, a collection of text/image/menu items in an expo
5 * Copyright 2022 Google LLC
9 #define LOG_CATEGORY LOGC_EXPO
17 #include <video_console.h>
18 #include <linux/input.h>
19 #include "scene_internal.h"
21 int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
25 scn = calloc(1, sizeof(struct scene));
27 return log_msg_ret("expo", -ENOMEM);
28 scn->name = strdup(name);
31 return log_msg_ret("name", -ENOMEM);
35 if (!abuf_realloc(&scn->buf, EXPO_MAX_CHARS + 1)) {
38 return log_msg_ret("buf", -ENOMEM);
40 abuf_init(&scn->entry_save);
42 INIT_LIST_HEAD(&scn->obj_head);
43 scn->id = resolve_id(exp, id);
45 list_add_tail(&scn->sibling, &exp->scene_head);
52 void scene_obj_destroy(struct scene_obj *obj)
54 if (obj->type == SCENEOBJT_MENU)
55 scene_menu_destroy((struct scene_obj_menu *)obj);
60 void scene_destroy(struct scene *scn)
62 struct scene_obj *obj, *next;
64 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
65 scene_obj_destroy(obj);
67 abuf_uninit(&scn->entry_save);
68 abuf_uninit(&scn->buf);
73 int scene_title_set(struct scene *scn, uint id)
80 int scene_obj_count(struct scene *scn)
82 return list_count_nodes(&scn->obj_head);
85 void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
87 struct scene_obj *obj;
89 list_for_each_entry(obj, &scn->obj_head, sibling) {
91 (type == SCENEOBJT_NONE || obj->type == type))
98 void *scene_obj_find_by_name(struct scene *scn, const char *name)
100 struct scene_obj *obj;
102 list_for_each_entry(obj, &scn->obj_head, sibling) {
103 if (!strcmp(name, obj->name))
110 int scene_obj_add(struct scene *scn, const char *name, uint id,
111 enum scene_obj_t type, uint size, struct scene_obj **objp)
113 struct scene_obj *obj;
115 obj = calloc(1, size);
117 return log_msg_ret("obj", -ENOMEM);
118 obj->name = strdup(name);
121 return log_msg_ret("name", -ENOMEM);
124 obj->id = resolve_id(scn->expo, id);
127 list_add_tail(&obj->sibling, &scn->obj_head);
133 int scene_img(struct scene *scn, const char *name, uint id, char *data,
134 struct scene_obj_img **imgp)
136 struct scene_obj_img *img;
139 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
140 sizeof(struct scene_obj_img),
141 (struct scene_obj **)&img);
143 return log_msg_ret("obj", ret);
153 int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
154 struct scene_obj_txt **txtp)
156 struct scene_obj_txt *txt;
159 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
160 sizeof(struct scene_obj_txt),
161 (struct scene_obj **)&txt);
163 return log_msg_ret("obj", ret);
165 txt->str_id = str_id;
173 int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
174 const char *str, struct scene_obj_txt **txtp)
176 struct scene_obj_txt *txt;
179 ret = expo_str(scn->expo, name, str_id, str);
181 return log_msg_ret("str", ret);
182 if (str_id && ret != str_id)
183 return log_msg_ret("id", -EEXIST);
186 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
187 sizeof(struct scene_obj_txt),
188 (struct scene_obj **)&txt);
190 return log_msg_ret("obj", ret);
192 txt->str_id = str_id;
200 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
203 struct scene_obj_txt *txt;
205 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
207 return log_msg_ret("find", -ENOENT);
208 txt->font_name = font_name;
209 txt->font_size = font_size;
214 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
216 struct scene_obj *obj;
218 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
220 return log_msg_ret("find", -ENOENT);
227 int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
229 struct scene_obj *obj;
231 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
233 return log_msg_ret("find", -ENOENT);
240 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
244 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
245 hide ? SCENEOF_HIDE : 0);
247 return log_msg_ret("flg", ret);
252 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
254 struct scene_obj *obj;
256 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
258 return log_msg_ret("find", -ENOENT);
265 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
267 struct scene_obj *obj;
269 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
271 return log_msg_ret("find", -ENOENT);
276 case SCENEOBJT_TEXTLINE:
278 case SCENEOBJT_IMAGE: {
279 struct scene_obj_img *img = (struct scene_obj_img *)obj;
283 video_bmp_get_info(img->data, &width, &height, &bpix);
288 case SCENEOBJT_TEXT: {
289 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
290 struct expo *exp = scn->expo;
291 struct vidconsole_bbox bbox;
295 str = expo_get_str(exp, txt->str_id);
297 return log_msg_ret("str", -ENOENT);
300 /* if there is no console, make it up */
307 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
308 txt->font_size, str, &bbox);
310 return log_msg_ret("mea", ret);
322 * scene_render_background() - Render the background for an object
324 * @obj: Object to render
325 * @box_only: true to show a box around the object, but keep the normal
326 * background colour inside
328 static void scene_render_background(struct scene_obj *obj, bool box_only)
330 struct expo *exp = obj->scene->expo;
331 const struct expo_theme *theme = &exp->theme;
332 struct vidconsole_bbox bbox, label_bbox;
333 struct udevice *dev = exp->display;
334 struct video_priv *vid_priv;
335 struct udevice *cons = exp->cons;
336 struct vidconsole_colour old;
337 enum colour_idx fore, back;
338 uint inset = theme->menu_inset;
340 /* draw a background for the object */
341 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
342 fore = VID_DARK_GREY;
345 fore = VID_LIGHT_GRAY;
349 /* see if this object wants to render a background */
350 if (scene_obj_calc_bbox(obj, &bbox, &label_bbox))
353 vidconsole_push_colour(cons, fore, back, &old);
354 vid_priv = dev_get_uclass_priv(dev);
355 video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
356 label_bbox.x1 + inset, label_bbox.y1 + inset,
357 vid_priv->colour_fg);
358 vidconsole_pop_colour(cons, &old);
360 video_fill_part(dev, label_bbox.x0, label_bbox.y0,
361 label_bbox.x1, label_bbox.y1,
362 vid_priv->colour_bg);
367 * scene_obj_render() - Render an object
370 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
372 struct scene *scn = obj->scene;
373 struct expo *exp = scn->expo;
374 const struct expo_theme *theme = &exp->theme;
375 struct udevice *dev = exp->display;
376 struct udevice *cons = text_mode ? NULL : exp->cons;
385 case SCENEOBJT_IMAGE: {
386 struct scene_obj_img *img = (struct scene_obj_img *)obj;
390 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
393 return log_msg_ret("img", ret);
396 case SCENEOBJT_TEXT: {
397 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
403 if (txt->font_name || txt->font_size) {
404 ret = vidconsole_select_font(cons,
408 ret = vidconsole_select_font(cons, NULL, 0);
410 if (ret && ret != -ENOSYS)
411 return log_msg_ret("font", ret);
412 str = expo_get_str(exp, txt->str_id);
414 struct video_priv *vid_priv;
415 struct vidconsole_colour old;
416 enum colour_idx fore, back;
418 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
422 fore = VID_LIGHT_GRAY;
426 vid_priv = dev_get_uclass_priv(dev);
427 if (obj->flags & SCENEOF_POINT) {
428 vidconsole_push_colour(cons, fore, back, &old);
429 video_fill_part(dev, x - theme->menu_inset, y,
432 vid_priv->colour_bg);
434 vidconsole_set_cursor_pos(cons, x, y);
435 vidconsole_put_string(cons, str);
436 if (obj->flags & SCENEOF_POINT)
437 vidconsole_pop_colour(cons, &old);
441 case SCENEOBJT_MENU: {
442 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
444 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
448 /* draw a background behind the menu items */
449 scene_render_background(obj, false);
452 * With a vidconsole, the text and item pointer are rendered as
453 * normal objects so we don't need to do anything here. The menu
454 * simply controls where they are positioned.
459 ret = scene_menu_display(menu);
461 return log_msg_ret("img", ret);
465 case SCENEOBJT_TEXTLINE:
466 if (obj->flags & SCENEOF_OPEN)
467 scene_render_background(obj, true);
474 int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
476 struct scene_obj *obj;
478 arr->label_width = 0;
479 list_for_each_entry(obj, &scn->obj_head, sibling) {
485 case SCENEOBJT_IMAGE:
488 case SCENEOBJT_MENU: {
489 struct scene_obj_menu *menu;
491 menu = (struct scene_obj_menu *)obj,
492 label_id = menu->title_id;
495 case SCENEOBJT_TEXTLINE: {
496 struct scene_obj_textline *tline;
498 tline = (struct scene_obj_textline *)obj,
499 label_id = tline->label_id;
507 ret = scene_obj_get_hw(scn, label_id, &width);
509 return log_msg_ret("hei", ret);
510 arr->label_width = max(arr->label_width, width);
517 int scene_arrange(struct scene *scn)
519 struct expo_arrange_info arr;
520 struct scene_obj *obj;
523 ret = scene_calc_arrange(scn, &arr);
525 return log_msg_ret("arr", ret);
527 list_for_each_entry(obj, &scn->obj_head, sibling) {
530 case SCENEOBJT_IMAGE:
533 case SCENEOBJT_MENU: {
534 struct scene_obj_menu *menu;
536 menu = (struct scene_obj_menu *)obj,
537 ret = scene_menu_arrange(scn, &arr, menu);
539 return log_msg_ret("arr", ret);
542 case SCENEOBJT_TEXTLINE: {
543 struct scene_obj_textline *tline;
545 tline = (struct scene_obj_textline *)obj,
546 ret = scene_textline_arrange(scn, &arr, tline);
548 return log_msg_ret("arr", ret);
557 int scene_render_deps(struct scene *scn, uint id)
559 struct scene_obj *obj;
564 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
566 return log_msg_ret("obj", -ENOENT);
568 if (!(obj->flags & SCENEOF_HIDE)) {
569 ret = scene_obj_render(obj, false);
570 if (ret && ret != -ENOTSUPP)
571 return log_msg_ret("ren", ret);
575 case SCENEOBJT_IMAGE:
579 scene_menu_render_deps(scn,
580 (struct scene_obj_menu *)obj);
582 case SCENEOBJT_TEXTLINE:
583 scene_textline_render_deps(scn,
584 (struct scene_obj_textline *)obj);
592 int scene_render(struct scene *scn)
594 struct expo *exp = scn->expo;
595 struct scene_obj *obj;
598 list_for_each_entry(obj, &scn->obj_head, sibling) {
599 if (!(obj->flags & SCENEOF_HIDE)) {
600 ret = scene_obj_render(obj, exp->text_mode);
601 if (ret && ret != -ENOTSUPP)
602 return log_msg_ret("ren", ret);
606 /* render any highlighted object on top of the others */
607 if (scn->highlight_id && !exp->text_mode) {
608 ret = scene_render_deps(scn, scn->highlight_id);
609 if (ret && ret != -ENOTSUPP)
610 return log_msg_ret("dep", ret);
617 * send_key_obj() - Handle a keypress for moving between objects
619 * @scn: Scene to receive the key
620 * @key: Key to send (KEYCODE_UP)
621 * @event: Returns resulting event from this keypress
622 * Returns: 0 if OK, -ve on error
624 static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
625 struct expo_action *event)
629 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
631 obj = list_entry(obj->sibling.prev,
632 struct scene_obj, sibling);
633 if (scene_obj_can_highlight(obj)) {
634 event->type = EXPOACT_POINT_OBJ;
635 event->select.id = obj->id;
636 log_debug("up to obj %d\n", event->select.id);
642 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
643 obj = list_entry(obj->sibling.next, struct scene_obj,
645 if (scene_obj_can_highlight(obj)) {
646 event->type = EXPOACT_POINT_OBJ;
647 event->select.id = obj->id;
648 log_debug("down to obj %d\n", event->select.id);
654 if (scene_obj_can_highlight(obj)) {
655 event->type = EXPOACT_OPEN;
656 event->select.id = obj->id;
657 log_debug("open obj %d\n", event->select.id);
661 event->type = EXPOACT_QUIT;
662 log_debug("obj quit\n");
667 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
669 struct scene_obj *obj;
672 event->type = EXPOACT_NONE;
675 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
678 if (scn->expo->popup) {
680 if (scn->highlight_id) {
681 obj = scene_obj_find(scn, scn->highlight_id,
687 if (!(obj->flags & SCENEOF_OPEN)) {
688 send_key_obj(scn, obj, key, event);
694 case SCENEOBJT_IMAGE:
697 case SCENEOBJT_MENU: {
698 struct scene_obj_menu *menu;
700 menu = (struct scene_obj_menu *)obj,
701 ret = scene_menu_send_key(scn, menu, key, event);
703 return log_msg_ret("key", ret);
706 case SCENEOBJT_TEXTLINE: {
707 struct scene_obj_textline *tline;
709 tline = (struct scene_obj_textline *)obj,
710 ret = scene_textline_send_key(scn, tline, key, event);
712 return log_msg_ret("key", ret);
719 list_for_each_entry(obj, &scn->obj_head, sibling) {
720 if (obj->type == SCENEOBJT_MENU) {
721 struct scene_obj_menu *menu;
723 menu = (struct scene_obj_menu *)obj,
724 ret = scene_menu_send_key(scn, menu, key, event);
726 return log_msg_ret("key", ret);
734 int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
735 struct vidconsole_bbox *label_bbox)
739 case SCENEOBJT_IMAGE:
742 case SCENEOBJT_MENU: {
743 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
745 scene_menu_calc_bbox(menu, bbox, label_bbox);
748 case SCENEOBJT_TEXTLINE: {
749 struct scene_obj_textline *tline;
751 tline = (struct scene_obj_textline *)obj;
752 scene_textline_calc_bbox(tline, bbox, label_bbox);
760 int scene_calc_dims(struct scene *scn, bool do_menus)
762 struct scene_obj *obj;
765 list_for_each_entry(obj, &scn->obj_head, sibling) {
769 case SCENEOBJT_IMAGE: {
773 ret = scene_obj_get_hw(scn, obj->id, &width);
775 return log_msg_ret("get", ret);
781 case SCENEOBJT_MENU: {
782 struct scene_obj_menu *menu;
785 menu = (struct scene_obj_menu *)obj;
787 ret = scene_menu_calc_dims(menu);
789 return log_msg_ret("men", ret);
793 case SCENEOBJT_TEXTLINE: {
794 struct scene_obj_textline *tline;
796 tline = (struct scene_obj_textline *)obj;
797 ret = scene_textline_calc_dims(tline);
799 return log_msg_ret("men", ret);
809 int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
811 struct scene_obj *obj;
814 /* Avoid error-checking optional items */
815 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
817 list_for_each_entry(obj, &scn->obj_head, sibling) {
820 case SCENEOBJT_IMAGE:
822 case SCENEOBJT_TEXTLINE:
825 scene_txt_set_font(scn, obj->id, NULL,
831 ret = scene_arrange(scn);
833 return log_msg_ret("arr", ret);
838 void scene_set_highlight_id(struct scene *scn, uint id)
840 scn->highlight_id = id;
843 void scene_highlight_first(struct scene *scn)
845 struct scene_obj *obj;
847 list_for_each_entry(obj, &scn->obj_head, sibling) {
848 if (scene_obj_can_highlight(obj)) {
849 scene_set_highlight_id(scn, obj->id);
855 static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
861 case SCENEOBJT_IMAGE:
865 case SCENEOBJT_TEXTLINE:
866 ret = scene_textline_open(scn,
867 (struct scene_obj_textline *)obj);
869 return log_msg_ret("op", ret);
876 int scene_set_open(struct scene *scn, uint id, bool open)
878 struct scene_obj *obj;
881 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
883 return log_msg_ret("find", -ENOENT);
886 ret = scene_obj_open(scn, obj);
888 return log_msg_ret("op", ret);
891 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
892 open ? SCENEOF_OPEN : 0);
894 return log_msg_ret("flg", ret);
899 int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
902 struct scene_obj *obj;
904 list_for_each_entry(obj, &scn->obj_head, sibling) {
907 ret = iter(obj, priv);
909 return log_msg_ret("itr", ret);
915 int scene_bbox_union(struct scene *scn, uint id, int inset,
916 struct vidconsole_bbox *bbox)
918 struct scene_obj *obj;
922 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
924 return log_msg_ret("obj", -ENOENT);
926 bbox->x0 = min(bbox->x0, obj->dim.x - inset);
927 bbox->y0 = min(bbox->y0, obj->dim.y);
928 bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
929 bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
931 bbox->x0 = obj->dim.x - inset;
932 bbox->y0 = obj->dim.y;
933 bbox->x1 = obj->dim.x + obj->dim.w + inset;
934 bbox->y1 = obj->dim.y + obj->dim.h;