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
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(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", -ENOMEM);
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", -ENOMEM);
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 else if (ret != str_id)
180 return log_msg_ret("id", -EEXIST);
182 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
183 sizeof(struct scene_obj_txt),
184 (struct scene_obj **)&txt);
186 return log_msg_ret("obj", -ENOMEM);
188 txt->str_id = str_id;
196 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
199 struct scene_obj_txt *txt;
201 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
203 return log_msg_ret("find", -ENOENT);
204 txt->font_name = font_name;
205 txt->font_size = font_size;
210 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
212 struct scene_obj *obj;
214 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
216 return log_msg_ret("find", -ENOENT);
223 int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
225 struct scene_obj *obj;
227 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
229 return log_msg_ret("find", -ENOENT);
236 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
240 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
241 hide ? SCENEOF_HIDE : 0);
243 return log_msg_ret("flg", ret);
248 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
250 struct scene_obj *obj;
252 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
254 return log_msg_ret("find", -ENOENT);
261 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
263 struct scene_obj *obj;
265 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
267 return log_msg_ret("find", -ENOENT);
273 case SCENEOBJT_IMAGE: {
274 struct scene_obj_img *img = (struct scene_obj_img *)obj;
278 video_bmp_get_info(img->data, &width, &height, &bpix);
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;
290 str = expo_get_str(exp, txt->str_id);
292 return log_msg_ret("str", -ENOENT);
295 /* if there is no console, make it up */
302 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
303 txt->font_size, str, &bbox);
305 return log_msg_ret("mea", ret);
317 * scene_obj_render() - Render an object
320 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
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;
335 case SCENEOBJT_IMAGE: {
336 struct scene_obj_img *img = (struct scene_obj_img *)obj;
340 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
343 return log_msg_ret("img", ret);
346 case SCENEOBJT_TEXT: {
347 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
353 if (txt->font_name || txt->font_size) {
354 ret = vidconsole_select_font(cons,
358 ret = vidconsole_select_font(cons, NULL, 0);
360 if (ret && ret != -ENOSYS)
361 return log_msg_ret("font", ret);
362 str = expo_get_str(exp, txt->str_id);
364 struct video_priv *vid_priv;
365 struct vidconsole_colour old;
366 enum colour_idx fore, back;
368 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
372 fore = VID_LIGHT_GRAY;
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,
382 vid_priv->colour_bg);
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);
391 case SCENEOBJT_MENU: {
392 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
394 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
398 /* draw a background behind the menu items */
399 scene_menu_render(menu);
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.
409 ret = scene_menu_display(menu);
411 return log_msg_ret("img", ret);
420 int scene_arrange(struct scene *scn)
422 struct scene_obj *obj;
425 list_for_each_entry(obj, &scn->obj_head, sibling) {
426 if (obj->type == SCENEOBJT_MENU) {
427 struct scene_obj_menu *menu;
429 menu = (struct scene_obj_menu *)obj,
430 ret = scene_menu_arrange(scn, menu);
432 return log_msg_ret("arr", ret);
439 int scene_render_deps(struct scene *scn, uint id)
441 struct scene_obj *obj;
446 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
448 return log_msg_ret("obj", -ENOENT);
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);
455 if (obj->type == SCENEOBJT_MENU)
456 scene_menu_render_deps(scn,
457 (struct scene_obj_menu *)obj);
463 int scene_render(struct scene *scn)
465 struct expo *exp = scn->expo;
466 struct scene_obj *obj;
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);
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);
488 * send_key_obj() - Handle a keypress for moving between objects
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
495 static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
496 struct expo_action *event)
500 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
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);
513 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
514 obj = list_entry(obj->sibling.next, struct scene_obj,
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);
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);
532 event->type = EXPOACT_QUIT;
533 log_debug("obj quit\n");
538 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
540 struct scene_obj_menu *menu;
541 struct scene_obj *obj;
544 event->type = EXPOACT_NONE;
547 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
550 if (scn->expo->popup) {
552 if (scn->highlight_id) {
553 obj = scene_obj_find(scn, scn->highlight_id,
559 if (!(obj->flags & SCENEOF_OPEN)) {
560 send_key_obj(scn, obj, key, event);
564 menu = (struct scene_obj_menu *)obj,
565 ret = scene_menu_send_key(scn, menu, key, event);
567 return log_msg_ret("key", ret);
571 list_for_each_entry(obj, &scn->obj_head, sibling) {
572 if (obj->type == SCENEOBJT_MENU) {
573 struct scene_obj_menu *menu;
575 menu = (struct scene_obj_menu *)obj,
576 ret = scene_menu_send_key(scn, menu, key, event);
578 return log_msg_ret("key", ret);
586 int scene_calc_dims(struct scene *scn, bool do_menus)
588 struct scene_obj *obj;
591 list_for_each_entry(obj, &scn->obj_head, sibling) {
595 case SCENEOBJT_IMAGE: {
599 ret = scene_obj_get_hw(scn, obj->id, &width);
601 return log_msg_ret("get", ret);
607 case SCENEOBJT_MENU: {
608 struct scene_obj_menu *menu;
611 menu = (struct scene_obj_menu *)obj;
613 ret = scene_menu_calc_dims(menu);
615 return log_msg_ret("men", ret);
625 int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
627 struct scene_obj *obj;
630 /* Avoid error-checking optional items */
631 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
633 list_for_each_entry(obj, &scn->obj_head, sibling) {
636 case SCENEOBJT_IMAGE:
640 scene_txt_set_font(scn, obj->id, NULL,
646 ret = scene_arrange(scn);
648 return log_msg_ret("arr", ret);
653 void scene_set_highlight_id(struct scene *scn, uint id)
655 scn->highlight_id = id;
658 void scene_highlight_first(struct scene *scn)
660 struct scene_obj *obj;
662 list_for_each_entry(obj, &scn->obj_head, sibling) {
665 scene_set_highlight_id(scn, obj->id);
673 int scene_set_open(struct scene *scn, uint id, bool open)
677 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
678 open ? SCENEOF_OPEN : 0);
680 return log_msg_ret("flg", ret);