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
6 * Written by Simon Glass <sjg@chromium.org>
9 #define LOG_CATEGORY LOGC_EXPO
18 #include <video_console.h>
19 #include <linux/input.h>
20 #include "scene_internal.h"
22 int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
26 scn = calloc(1, sizeof(struct scene));
28 return log_msg_ret("expo", -ENOMEM);
29 scn->name = strdup(name);
32 return log_msg_ret("name", -ENOMEM);
35 INIT_LIST_HEAD(&scn->obj_head);
36 scn->id = resolve_id(exp, id);
38 list_add_tail(&scn->sibling, &exp->scene_head);
45 void scene_obj_destroy(struct scene_obj *obj)
47 if (obj->type == SCENEOBJT_MENU)
48 scene_menu_destroy((struct scene_obj_menu *)obj);
53 void scene_destroy(struct scene *scn)
55 struct scene_obj *obj, *next;
57 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
58 scene_obj_destroy(obj);
64 int scene_title_set(struct scene *scn, uint id)
71 int scene_obj_count(struct scene *scn)
73 struct scene_obj *obj;
76 list_for_each_entry(obj, &scn->obj_head, sibling)
82 void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
84 struct scene_obj *obj;
86 list_for_each_entry(obj, &scn->obj_head, sibling) {
88 (type == SCENEOBJT_NONE || obj->type == type))
95 void *scene_obj_find_by_name(struct scene *scn, const char *name)
97 struct scene_obj *obj;
99 list_for_each_entry(obj, &scn->obj_head, sibling) {
100 if (!strcmp(name, obj->name))
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)
110 struct scene_obj *obj;
112 obj = calloc(1, size);
114 return log_msg_ret("obj", -ENOMEM);
115 obj->name = strdup(name);
118 return log_msg_ret("name", -ENOMEM);
121 obj->id = resolve_id(scn->expo, id);
124 list_add_tail(&obj->sibling, &scn->obj_head);
130 int scene_img(struct scene *scn, const char *name, uint id, char *data,
131 struct scene_obj_img **imgp)
133 struct scene_obj_img *img;
136 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
137 sizeof(struct scene_obj_img),
138 (struct scene_obj **)&img);
140 return log_msg_ret("obj", ret);
150 int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
151 struct scene_obj_txt **txtp)
153 struct scene_obj_txt *txt;
156 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
157 sizeof(struct scene_obj_txt),
158 (struct scene_obj **)&txt);
160 return log_msg_ret("obj", ret);
162 txt->str_id = str_id;
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)
173 struct scene_obj_txt *txt;
176 ret = expo_str(scn->expo, name, str_id, str);
178 return log_msg_ret("str", ret);
179 if (str_id && ret != str_id)
180 return log_msg_ret("id", -EEXIST);
183 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
184 sizeof(struct scene_obj_txt),
185 (struct scene_obj **)&txt);
187 return log_msg_ret("obj", ret);
189 txt->str_id = str_id;
197 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
200 struct scene_obj_txt *txt;
202 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
204 return log_msg_ret("find", -ENOENT);
205 txt->font_name = font_name;
206 txt->font_size = font_size;
211 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
213 struct scene_obj *obj;
215 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
217 return log_msg_ret("find", -ENOENT);
224 int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
226 struct scene_obj *obj;
228 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
230 return log_msg_ret("find", -ENOENT);
237 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
241 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
242 hide ? SCENEOF_HIDE : 0);
244 return log_msg_ret("flg", ret);
249 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
251 struct scene_obj *obj;
253 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
255 return log_msg_ret("find", -ENOENT);
262 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
264 struct scene_obj *obj;
266 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
268 return log_msg_ret("find", -ENOENT);
274 case SCENEOBJT_IMAGE: {
275 struct scene_obj_img *img = (struct scene_obj_img *)obj;
279 video_bmp_get_info(img->data, &width, &height, &bpix);
284 case SCENEOBJT_TEXT: {
285 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
286 struct expo *exp = scn->expo;
287 struct vidconsole_bbox bbox;
291 str = expo_get_str(exp, txt->str_id);
293 return log_msg_ret("str", -ENOENT);
296 /* if there is no console, make it up */
303 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
304 txt->font_size, str, &bbox);
306 return log_msg_ret("mea", ret);
318 * scene_render_background() - Render the background for an object
320 * @obj: Object to render
322 static void scene_render_background(struct scene_obj *obj)
324 struct expo *exp = obj->scene->expo;
325 const struct expo_theme *theme = &exp->theme;
326 struct vidconsole_bbox bbox, label_bbox;
327 struct udevice *dev = exp->display;
328 struct video_priv *vid_priv;
329 struct udevice *cons = exp->cons;
330 struct vidconsole_colour old;
331 enum colour_idx fore, back;
332 uint inset = theme->menu_inset;
334 /* draw a background for the object */
335 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
339 fore = VID_LIGHT_GRAY;
343 /* see if this object wants to render a background */
344 if (scene_obj_calc_bbox(obj, &bbox, &label_bbox))
347 vidconsole_push_colour(cons, fore, back, &old);
348 vid_priv = dev_get_uclass_priv(dev);
349 video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
350 label_bbox.x1 + inset, label_bbox.y1 + inset,
351 vid_priv->colour_fg);
352 vidconsole_pop_colour(cons, &old);
356 * scene_obj_render() - Render an object
359 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
361 struct scene *scn = obj->scene;
362 struct expo *exp = scn->expo;
363 const struct expo_theme *theme = &exp->theme;
364 struct udevice *dev = exp->display;
365 struct udevice *cons = text_mode ? NULL : exp->cons;
374 case SCENEOBJT_IMAGE: {
375 struct scene_obj_img *img = (struct scene_obj_img *)obj;
379 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
382 return log_msg_ret("img", ret);
385 case SCENEOBJT_TEXT: {
386 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
392 if (txt->font_name || txt->font_size) {
393 ret = vidconsole_select_font(cons,
397 ret = vidconsole_select_font(cons, NULL, 0);
399 if (ret && ret != -ENOSYS)
400 return log_msg_ret("font", ret);
401 str = expo_get_str(exp, txt->str_id);
403 struct video_priv *vid_priv;
404 struct vidconsole_colour old;
405 enum colour_idx fore, back;
407 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
411 fore = VID_LIGHT_GRAY;
415 vid_priv = dev_get_uclass_priv(dev);
416 if (obj->flags & SCENEOF_POINT) {
417 vidconsole_push_colour(cons, fore, back, &old);
418 video_fill_part(dev, x - theme->menu_inset, y,
421 vid_priv->colour_bg);
423 vidconsole_set_cursor_pos(cons, x, y);
424 vidconsole_put_string(cons, str);
425 if (obj->flags & SCENEOF_POINT)
426 vidconsole_pop_colour(cons, &old);
430 case SCENEOBJT_MENU: {
431 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
433 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
437 /* draw a background behind the menu items */
438 scene_render_background(obj);
441 * With a vidconsole, the text and item pointer are rendered as
442 * normal objects so we don't need to do anything here. The menu
443 * simply controls where they are positioned.
448 ret = scene_menu_display(menu);
450 return log_msg_ret("img", ret);
459 int scene_arrange(struct scene *scn)
461 struct scene_obj *obj;
464 list_for_each_entry(obj, &scn->obj_head, sibling) {
467 case SCENEOBJT_IMAGE:
470 case SCENEOBJT_MENU: {
471 struct scene_obj_menu *menu;
473 menu = (struct scene_obj_menu *)obj,
474 ret = scene_menu_arrange(scn, menu);
476 return log_msg_ret("arr", ret);
485 int scene_render_deps(struct scene *scn, uint id)
487 struct scene_obj *obj;
492 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
494 return log_msg_ret("obj", -ENOENT);
496 if (!(obj->flags & SCENEOF_HIDE)) {
497 ret = scene_obj_render(obj, false);
498 if (ret && ret != -ENOTSUPP)
499 return log_msg_ret("ren", ret);
503 case SCENEOBJT_IMAGE:
507 scene_menu_render_deps(scn,
508 (struct scene_obj_menu *)obj);
516 int scene_render(struct scene *scn)
518 struct expo *exp = scn->expo;
519 struct scene_obj *obj;
522 list_for_each_entry(obj, &scn->obj_head, sibling) {
523 if (!(obj->flags & SCENEOF_HIDE)) {
524 ret = scene_obj_render(obj, exp->text_mode);
525 if (ret && ret != -ENOTSUPP)
526 return log_msg_ret("ren", ret);
530 /* render any highlighted object on top of the others */
531 if (scn->highlight_id && !exp->text_mode) {
532 ret = scene_render_deps(scn, scn->highlight_id);
533 if (ret && ret != -ENOTSUPP)
534 return log_msg_ret("dep", ret);
541 * send_key_obj() - Handle a keypress for moving between objects
543 * @scn: Scene to receive the key
544 * @key: Key to send (KEYCODE_UP)
545 * @event: Returns resulting event from this keypress
546 * Returns: 0 if OK, -ve on error
548 static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
549 struct expo_action *event)
553 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
555 obj = list_entry(obj->sibling.prev,
556 struct scene_obj, sibling);
557 if (scene_obj_can_highlight(obj)) {
558 event->type = EXPOACT_POINT_OBJ;
559 event->select.id = obj->id;
560 log_debug("up to obj %d\n", event->select.id);
566 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
567 obj = list_entry(obj->sibling.next, struct scene_obj,
569 if (scene_obj_can_highlight(obj)) {
570 event->type = EXPOACT_POINT_OBJ;
571 event->select.id = obj->id;
572 log_debug("down to obj %d\n", event->select.id);
578 if (scene_obj_can_highlight(obj)) {
579 event->type = EXPOACT_OPEN;
580 event->select.id = obj->id;
581 log_debug("open obj %d\n", event->select.id);
585 event->type = EXPOACT_QUIT;
586 log_debug("obj quit\n");
591 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
593 struct scene_obj *obj;
596 event->type = EXPOACT_NONE;
599 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
602 if (scn->expo->popup) {
604 if (scn->highlight_id) {
605 obj = scene_obj_find(scn, scn->highlight_id,
611 if (!(obj->flags & SCENEOF_OPEN)) {
612 send_key_obj(scn, obj, key, event);
618 case SCENEOBJT_IMAGE:
621 case SCENEOBJT_MENU: {
622 struct scene_obj_menu *menu;
624 menu = (struct scene_obj_menu *)obj,
625 ret = scene_menu_send_key(scn, menu, key, event);
627 return log_msg_ret("key", ret);
634 list_for_each_entry(obj, &scn->obj_head, sibling) {
635 if (obj->type == SCENEOBJT_MENU) {
636 struct scene_obj_menu *menu;
638 menu = (struct scene_obj_menu *)obj,
639 ret = scene_menu_send_key(scn, menu, key, event);
641 return log_msg_ret("key", ret);
649 int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
650 struct vidconsole_bbox *label_bbox)
654 case SCENEOBJT_IMAGE:
657 case SCENEOBJT_MENU: {
658 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
660 scene_menu_calc_bbox(menu, bbox, label_bbox);
668 int scene_calc_dims(struct scene *scn, bool do_menus)
670 struct scene_obj *obj;
673 list_for_each_entry(obj, &scn->obj_head, sibling) {
677 case SCENEOBJT_IMAGE: {
681 ret = scene_obj_get_hw(scn, obj->id, &width);
683 return log_msg_ret("get", ret);
689 case SCENEOBJT_MENU: {
690 struct scene_obj_menu *menu;
693 menu = (struct scene_obj_menu *)obj;
695 ret = scene_menu_calc_dims(menu);
697 return log_msg_ret("men", ret);
707 int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
709 struct scene_obj *obj;
712 /* Avoid error-checking optional items */
713 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
715 list_for_each_entry(obj, &scn->obj_head, sibling) {
718 case SCENEOBJT_IMAGE:
722 scene_txt_set_font(scn, obj->id, NULL,
728 ret = scene_arrange(scn);
730 return log_msg_ret("arr", ret);
735 void scene_set_highlight_id(struct scene *scn, uint id)
737 scn->highlight_id = id;
740 void scene_highlight_first(struct scene *scn)
742 struct scene_obj *obj;
744 list_for_each_entry(obj, &scn->obj_head, sibling) {
745 if (scene_obj_can_highlight(obj)) {
746 scene_set_highlight_id(scn, obj->id);
752 int scene_set_open(struct scene *scn, uint id, bool open)
756 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
757 open ? SCENEOF_OPEN : 0);
759 return log_msg_ret("flg", ret);
764 int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
767 struct scene_obj *obj;
769 list_for_each_entry(obj, &scn->obj_head, sibling) {
772 ret = iter(obj, priv);
774 return log_msg_ret("itr", ret);
780 int scene_bbox_union(struct scene *scn, uint id, int inset,
781 struct vidconsole_bbox *bbox)
783 struct scene_obj *obj;
787 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
789 return log_msg_ret("obj", -ENOENT);
791 bbox->x0 = min(bbox->x0, obj->dim.x - inset);
792 bbox->y0 = min(bbox->y0, obj->dim.y);
793 bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
794 bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
796 bbox->x0 = obj->dim.x - inset;
797 bbox->y0 = obj->dim.y;
798 bbox->x1 = obj->dim.x + obj->dim.w + inset;
799 bbox->y1 = obj->dim.y + obj->dim.h;