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_obj_count(struct scene *scn)
75 return list_count_nodes(&scn->obj_head);
78 void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
80 struct scene_obj *obj;
82 list_for_each_entry(obj, &scn->obj_head, sibling) {
84 (type == SCENEOBJT_NONE || obj->type == type))
91 void *scene_obj_find_by_name(struct scene *scn, const char *name)
93 struct scene_obj *obj;
95 list_for_each_entry(obj, &scn->obj_head, sibling) {
96 if (!strcmp(name, obj->name))
103 int scene_obj_add(struct scene *scn, const char *name, uint id,
104 enum scene_obj_t type, uint size, struct scene_obj **objp)
106 struct scene_obj *obj;
108 obj = calloc(1, size);
110 return log_msg_ret("obj", -ENOMEM);
111 obj->name = strdup(name);
114 return log_msg_ret("name", -ENOMEM);
117 obj->id = resolve_id(scn->expo, id);
120 list_add_tail(&obj->sibling, &scn->obj_head);
126 int scene_img(struct scene *scn, const char *name, uint id, char *data,
127 struct scene_obj_img **imgp)
129 struct scene_obj_img *img;
132 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
133 sizeof(struct scene_obj_img),
134 (struct scene_obj **)&img);
136 return log_msg_ret("obj", ret);
146 int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
147 struct scene_obj_txt **txtp)
149 struct scene_obj_txt *txt;
152 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
153 sizeof(struct scene_obj_txt),
154 (struct scene_obj **)&txt);
156 return log_msg_ret("obj", ret);
158 txt->str_id = str_id;
166 int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
167 const char *str, struct scene_obj_txt **txtp)
169 struct scene_obj_txt *txt;
172 ret = expo_str(scn->expo, name, str_id, str);
174 return log_msg_ret("str", ret);
175 if (str_id && ret != str_id)
176 return log_msg_ret("id", -EEXIST);
179 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
180 sizeof(struct scene_obj_txt),
181 (struct scene_obj **)&txt);
183 return log_msg_ret("obj", ret);
185 txt->str_id = str_id;
193 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
196 struct scene_obj_txt *txt;
198 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
200 return log_msg_ret("find", -ENOENT);
201 txt->font_name = font_name;
202 txt->font_size = font_size;
207 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
209 struct scene_obj *obj;
211 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
213 return log_msg_ret("find", -ENOENT);
220 int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
222 struct scene_obj *obj;
224 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
226 return log_msg_ret("find", -ENOENT);
233 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
237 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
238 hide ? SCENEOF_HIDE : 0);
240 return log_msg_ret("flg", ret);
245 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
247 struct scene_obj *obj;
249 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
251 return log_msg_ret("find", -ENOENT);
258 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
260 struct scene_obj *obj;
262 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
264 return log_msg_ret("find", -ENOENT);
269 case SCENEOBJT_TEXTLINE:
271 case SCENEOBJT_IMAGE: {
272 struct scene_obj_img *img = (struct scene_obj_img *)obj;
276 video_bmp_get_info(img->data, &width, &height, &bpix);
281 case SCENEOBJT_TEXT: {
282 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
283 struct expo *exp = scn->expo;
284 struct vidconsole_bbox bbox;
288 str = expo_get_str(exp, txt->str_id);
290 return log_msg_ret("str", -ENOENT);
293 /* if there is no console, make it up */
300 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
301 txt->font_size, str, &bbox);
303 return log_msg_ret("mea", ret);
315 * scene_render_background() - Render the background for an object
317 * @obj: Object to render
318 * @box_only: true to show a box around the object, but keep the normal
319 * background colour inside
321 static void scene_render_background(struct scene_obj *obj, bool box_only)
323 struct expo *exp = obj->scene->expo;
324 const struct expo_theme *theme = &exp->theme;
325 struct vidconsole_bbox bbox, label_bbox;
326 struct udevice *dev = exp->display;
327 struct video_priv *vid_priv;
328 struct udevice *cons = exp->cons;
329 struct vidconsole_colour old;
330 enum colour_idx fore, back;
331 uint inset = theme->menu_inset;
333 /* draw a background for the object */
334 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
335 fore = VID_DARK_GREY;
338 fore = VID_LIGHT_GRAY;
342 /* see if this object wants to render a background */
343 if (scene_obj_calc_bbox(obj, &bbox, &label_bbox))
346 vidconsole_push_colour(cons, fore, back, &old);
347 vid_priv = dev_get_uclass_priv(dev);
348 video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
349 label_bbox.x1 + inset, label_bbox.y1 + inset,
350 vid_priv->colour_fg);
351 vidconsole_pop_colour(cons, &old);
353 video_fill_part(dev, label_bbox.x0, label_bbox.y0,
354 label_bbox.x1, label_bbox.y1,
355 vid_priv->colour_bg);
360 * scene_obj_render() - Render an object
363 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
365 struct scene *scn = obj->scene;
366 struct expo *exp = scn->expo;
367 const struct expo_theme *theme = &exp->theme;
368 struct udevice *dev = exp->display;
369 struct udevice *cons = text_mode ? NULL : exp->cons;
378 case SCENEOBJT_IMAGE: {
379 struct scene_obj_img *img = (struct scene_obj_img *)obj;
383 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
386 return log_msg_ret("img", ret);
389 case SCENEOBJT_TEXT: {
390 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
396 if (txt->font_name || txt->font_size) {
397 ret = vidconsole_select_font(cons,
401 ret = vidconsole_select_font(cons, NULL, 0);
403 if (ret && ret != -ENOSYS)
404 return log_msg_ret("font", ret);
405 str = expo_get_str(exp, txt->str_id);
407 struct video_priv *vid_priv;
408 struct vidconsole_colour old;
409 enum colour_idx fore, back;
411 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
415 fore = VID_LIGHT_GRAY;
419 vid_priv = dev_get_uclass_priv(dev);
420 if (obj->flags & SCENEOF_POINT) {
421 vidconsole_push_colour(cons, fore, back, &old);
422 video_fill_part(dev, x - theme->menu_inset, y,
425 vid_priv->colour_bg);
427 vidconsole_set_cursor_pos(cons, x, y);
428 vidconsole_put_string(cons, str);
429 if (obj->flags & SCENEOF_POINT)
430 vidconsole_pop_colour(cons, &old);
434 case SCENEOBJT_MENU: {
435 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
437 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
441 /* draw a background behind the menu items */
442 scene_render_background(obj, false);
445 * With a vidconsole, the text and item pointer are rendered as
446 * normal objects so we don't need to do anything here. The menu
447 * simply controls where they are positioned.
452 ret = scene_menu_display(menu);
454 return log_msg_ret("img", ret);
458 case SCENEOBJT_TEXTLINE:
459 if (obj->flags & SCENEOF_OPEN)
460 scene_render_background(obj, true);
467 int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
469 struct scene_obj *obj;
471 arr->label_width = 0;
472 list_for_each_entry(obj, &scn->obj_head, sibling) {
478 case SCENEOBJT_IMAGE:
481 case SCENEOBJT_MENU: {
482 struct scene_obj_menu *menu;
484 menu = (struct scene_obj_menu *)obj,
485 label_id = menu->title_id;
488 case SCENEOBJT_TEXTLINE: {
489 struct scene_obj_textline *tline;
491 tline = (struct scene_obj_textline *)obj,
492 label_id = tline->label_id;
500 ret = scene_obj_get_hw(scn, label_id, &width);
502 return log_msg_ret("hei", ret);
503 arr->label_width = max(arr->label_width, width);
510 int scene_arrange(struct scene *scn)
512 struct expo_arrange_info arr;
513 struct scene_obj *obj;
516 ret = scene_calc_arrange(scn, &arr);
518 return log_msg_ret("arr", ret);
520 list_for_each_entry(obj, &scn->obj_head, sibling) {
523 case SCENEOBJT_IMAGE:
526 case SCENEOBJT_MENU: {
527 struct scene_obj_menu *menu;
529 menu = (struct scene_obj_menu *)obj,
530 ret = scene_menu_arrange(scn, &arr, menu);
532 return log_msg_ret("arr", ret);
535 case SCENEOBJT_TEXTLINE: {
536 struct scene_obj_textline *tline;
538 tline = (struct scene_obj_textline *)obj,
539 ret = scene_textline_arrange(scn, &arr, tline);
541 return log_msg_ret("arr", ret);
550 int scene_render_deps(struct scene *scn, uint id)
552 struct scene_obj *obj;
557 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
559 return log_msg_ret("obj", -ENOENT);
561 if (!(obj->flags & SCENEOF_HIDE)) {
562 ret = scene_obj_render(obj, false);
563 if (ret && ret != -ENOTSUPP)
564 return log_msg_ret("ren", ret);
568 case SCENEOBJT_IMAGE:
572 scene_menu_render_deps(scn,
573 (struct scene_obj_menu *)obj);
575 case SCENEOBJT_TEXTLINE:
576 scene_textline_render_deps(scn,
577 (struct scene_obj_textline *)obj);
585 int scene_render(struct scene *scn)
587 struct expo *exp = scn->expo;
588 struct scene_obj *obj;
591 list_for_each_entry(obj, &scn->obj_head, sibling) {
592 if (!(obj->flags & SCENEOF_HIDE)) {
593 ret = scene_obj_render(obj, exp->text_mode);
594 if (ret && ret != -ENOTSUPP)
595 return log_msg_ret("ren", ret);
599 /* render any highlighted object on top of the others */
600 if (scn->highlight_id && !exp->text_mode) {
601 ret = scene_render_deps(scn, scn->highlight_id);
602 if (ret && ret != -ENOTSUPP)
603 return log_msg_ret("dep", ret);
610 * send_key_obj() - Handle a keypress for moving between objects
612 * @scn: Scene to receive the key
613 * @key: Key to send (KEYCODE_UP)
614 * @event: Returns resulting event from this keypress
615 * Returns: 0 if OK, -ve on error
617 static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
618 struct expo_action *event)
622 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
624 obj = list_entry(obj->sibling.prev,
625 struct scene_obj, sibling);
626 if (scene_obj_can_highlight(obj)) {
627 event->type = EXPOACT_POINT_OBJ;
628 event->select.id = obj->id;
629 log_debug("up to obj %d\n", event->select.id);
635 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
636 obj = list_entry(obj->sibling.next, struct scene_obj,
638 if (scene_obj_can_highlight(obj)) {
639 event->type = EXPOACT_POINT_OBJ;
640 event->select.id = obj->id;
641 log_debug("down to obj %d\n", event->select.id);
647 if (scene_obj_can_highlight(obj)) {
648 event->type = EXPOACT_OPEN;
649 event->select.id = obj->id;
650 log_debug("open obj %d\n", event->select.id);
654 event->type = EXPOACT_QUIT;
655 log_debug("obj quit\n");
660 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
662 struct scene_obj *obj;
665 event->type = EXPOACT_NONE;
668 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
671 if (scn->expo->popup) {
673 if (scn->highlight_id) {
674 obj = scene_obj_find(scn, scn->highlight_id,
680 if (!(obj->flags & SCENEOF_OPEN)) {
681 send_key_obj(scn, obj, key, event);
687 case SCENEOBJT_IMAGE:
690 case SCENEOBJT_MENU: {
691 struct scene_obj_menu *menu;
693 menu = (struct scene_obj_menu *)obj,
694 ret = scene_menu_send_key(scn, menu, key, event);
696 return log_msg_ret("key", ret);
699 case SCENEOBJT_TEXTLINE: {
700 struct scene_obj_textline *tline;
702 tline = (struct scene_obj_textline *)obj,
703 ret = scene_textline_send_key(scn, tline, key, event);
705 return log_msg_ret("key", ret);
712 list_for_each_entry(obj, &scn->obj_head, sibling) {
713 if (obj->type == SCENEOBJT_MENU) {
714 struct scene_obj_menu *menu;
716 menu = (struct scene_obj_menu *)obj,
717 ret = scene_menu_send_key(scn, menu, key, event);
719 return log_msg_ret("key", ret);
727 int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
728 struct vidconsole_bbox *label_bbox)
732 case SCENEOBJT_IMAGE:
735 case SCENEOBJT_MENU: {
736 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
738 scene_menu_calc_bbox(menu, bbox, label_bbox);
741 case SCENEOBJT_TEXTLINE: {
742 struct scene_obj_textline *tline;
744 tline = (struct scene_obj_textline *)obj;
745 scene_textline_calc_bbox(tline, bbox, label_bbox);
753 int scene_calc_dims(struct scene *scn, bool do_menus)
755 struct scene_obj *obj;
758 list_for_each_entry(obj, &scn->obj_head, sibling) {
762 case SCENEOBJT_IMAGE: {
766 ret = scene_obj_get_hw(scn, obj->id, &width);
768 return log_msg_ret("get", ret);
774 case SCENEOBJT_MENU: {
775 struct scene_obj_menu *menu;
778 menu = (struct scene_obj_menu *)obj;
780 ret = scene_menu_calc_dims(menu);
782 return log_msg_ret("men", ret);
786 case SCENEOBJT_TEXTLINE: {
787 struct scene_obj_textline *tline;
789 tline = (struct scene_obj_textline *)obj;
790 ret = scene_textline_calc_dims(tline);
792 return log_msg_ret("men", ret);
802 int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
804 struct scene_obj *obj;
807 /* Avoid error-checking optional items */
808 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
810 list_for_each_entry(obj, &scn->obj_head, sibling) {
813 case SCENEOBJT_IMAGE:
815 case SCENEOBJT_TEXTLINE:
818 scene_txt_set_font(scn, obj->id, NULL,
824 ret = scene_arrange(scn);
826 return log_msg_ret("arr", ret);
831 void scene_set_highlight_id(struct scene *scn, uint id)
833 scn->highlight_id = id;
836 void scene_highlight_first(struct scene *scn)
838 struct scene_obj *obj;
840 list_for_each_entry(obj, &scn->obj_head, sibling) {
841 if (scene_obj_can_highlight(obj)) {
842 scene_set_highlight_id(scn, obj->id);
848 static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
854 case SCENEOBJT_IMAGE:
858 case SCENEOBJT_TEXTLINE:
859 ret = scene_textline_open(scn,
860 (struct scene_obj_textline *)obj);
862 return log_msg_ret("op", ret);
869 int scene_set_open(struct scene *scn, uint id, bool open)
871 struct scene_obj *obj;
874 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
876 return log_msg_ret("find", -ENOENT);
879 ret = scene_obj_open(scn, obj);
881 return log_msg_ret("op", ret);
884 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
885 open ? SCENEOF_OPEN : 0);
887 return log_msg_ret("flg", ret);
892 int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
895 struct scene_obj *obj;
897 list_for_each_entry(obj, &scn->obj_head, sibling) {
900 ret = iter(obj, priv);
902 return log_msg_ret("itr", ret);
908 int scene_bbox_union(struct scene *scn, uint id, int inset,
909 struct vidconsole_bbox *bbox)
911 struct scene_obj *obj;
915 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
917 return log_msg_ret("obj", -ENOENT);
919 bbox->x0 = min(bbox->x0, obj->dim.x - inset);
920 bbox->y0 = min(bbox->y0, obj->dim.y);
921 bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
922 bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
924 bbox->x0 = obj->dim.x - inset;
925 bbox->y0 = obj->dim.y;
926 bbox->x1 = obj->dim.x + obj->dim.w + inset;
927 bbox->y1 = obj->dim.y + obj->dim.h;