blob: aacdccdce17106cbd69393001f56530ad09c95cf [file] [log] [blame]
Simon Glass0a4d14b2023-01-06 08:52:37 -06001// 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
Simon Glassfe4c1e22023-06-01 10:22:43 -06009#define LOG_CATEGORY LOGC_EXPO
10
Simon Glass0a4d14b2023-01-06 08:52:37 -060011#include <dm.h>
12#include <expo.h>
13#include <malloc.h>
14#include <mapmem.h>
Simon Glassf0e1e8c2023-06-01 10:22:59 -060015#include <menu.h>
Simon Glass0a4d14b2023-01-06 08:52:37 -060016#include <video.h>
17#include <video_console.h>
18#include <linux/input.h>
19#include "scene_internal.h"
20
Simon Glass0a4d14b2023-01-06 08:52:37 -060021int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
22{
23 struct scene *scn;
24
25 scn = calloc(1, sizeof(struct scene));
26 if (!scn)
27 return log_msg_ret("expo", -ENOMEM);
28 scn->name = strdup(name);
29 if (!scn->name) {
30 free(scn);
31 return log_msg_ret("name", -ENOMEM);
32 }
33
Simon Glass6651e942025-05-01 07:37:01 -060034 if (!abuf_init_size(&scn->buf, EXPO_MAX_CHARS + 1)) {
Simon Glassa968f5f2023-10-01 19:13:31 -060035 free(scn->name);
36 free(scn);
37 return log_msg_ret("buf", -ENOMEM);
38 }
39 abuf_init(&scn->entry_save);
40
Simon Glass0a4d14b2023-01-06 08:52:37 -060041 INIT_LIST_HEAD(&scn->obj_head);
42 scn->id = resolve_id(exp, id);
43 scn->expo = exp;
44 list_add_tail(&scn->sibling, &exp->scene_head);
45
46 *scnp = scn;
47
48 return scn->id;
49}
50
51void scene_obj_destroy(struct scene_obj *obj)
52{
53 if (obj->type == SCENEOBJT_MENU)
54 scene_menu_destroy((struct scene_obj_menu *)obj);
55 free(obj->name);
56 free(obj);
57}
58
59void scene_destroy(struct scene *scn)
60{
61 struct scene_obj *obj, *next;
62
63 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
64 scene_obj_destroy(obj);
65
Simon Glassa968f5f2023-10-01 19:13:31 -060066 abuf_uninit(&scn->entry_save);
67 abuf_uninit(&scn->buf);
Simon Glass0a4d14b2023-01-06 08:52:37 -060068 free(scn->name);
Simon Glass0a4d14b2023-01-06 08:52:37 -060069 free(scn);
70}
71
Simon Glass0a4d14b2023-01-06 08:52:37 -060072int scene_obj_count(struct scene *scn)
73{
Sughosh Ganuebb1c202024-08-28 22:24:22 +053074 return list_count_nodes(&scn->obj_head);
Simon Glass0a4d14b2023-01-06 08:52:37 -060075}
76
Simon Glass45ff0bc2023-08-14 16:40:21 -060077void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
Simon Glass0a4d14b2023-01-06 08:52:37 -060078{
79 struct scene_obj *obj;
80
81 list_for_each_entry(obj, &scn->obj_head, sibling) {
82 if (obj->id == id &&
83 (type == SCENEOBJT_NONE || obj->type == type))
84 return obj;
85 }
86
87 return NULL;
88}
89
Simon Glassc8925112023-06-01 10:23:02 -060090void *scene_obj_find_by_name(struct scene *scn, const char *name)
91{
92 struct scene_obj *obj;
93
94 list_for_each_entry(obj, &scn->obj_head, sibling) {
95 if (!strcmp(name, obj->name))
96 return obj;
97 }
98
99 return NULL;
100}
101
Simon Glass0a4d14b2023-01-06 08:52:37 -0600102int scene_obj_add(struct scene *scn, const char *name, uint id,
103 enum scene_obj_t type, uint size, struct scene_obj **objp)
104{
105 struct scene_obj *obj;
106
107 obj = calloc(1, size);
108 if (!obj)
109 return log_msg_ret("obj", -ENOMEM);
110 obj->name = strdup(name);
111 if (!obj->name) {
112 free(obj);
113 return log_msg_ret("name", -ENOMEM);
114 }
115
116 obj->id = resolve_id(scn->expo, id);
117 obj->scene = scn;
118 obj->type = type;
119 list_add_tail(&obj->sibling, &scn->obj_head);
120 *objp = obj;
121
122 return obj->id;
123}
124
125int scene_img(struct scene *scn, const char *name, uint id, char *data,
126 struct scene_obj_img **imgp)
127{
128 struct scene_obj_img *img;
129 int ret;
130
131 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
132 sizeof(struct scene_obj_img),
133 (struct scene_obj **)&img);
134 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600135 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600136
137 img->data = data;
138
139 if (imgp)
140 *imgp = img;
141
142 return img->obj.id;
143}
144
Simon Glass9ef02aa2025-05-02 08:46:37 -0600145int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen,
146 const char *name, uint str_id, const char *str)
147{
148 int ret;
149
150 if (str) {
151 ret = expo_str(exp, name, str_id, str);
152 if (ret < 0)
153 return log_msg_ret("str", ret);
154 if (str_id && ret != str_id)
155 return log_msg_ret("id", -EEXIST);
156 str_id = ret;
157 } else {
158 ret = resolve_id(exp, str_id);
159 if (ret < 0)
160 return log_msg_ret("nst", ret);
161 if (str_id && ret != str_id)
162 return log_msg_ret("nid", -EEXIST);
163 }
164
165 gen->str_id = str_id;
166
167 return 0;
168}
169
Simon Glass0a4d14b2023-01-06 08:52:37 -0600170int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
171 struct scene_obj_txt **txtp)
172{
173 struct scene_obj_txt *txt;
174 int ret;
175
176 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
177 sizeof(struct scene_obj_txt),
178 (struct scene_obj **)&txt);
179 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600180 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600181
Simon Glass9ef02aa2025-05-02 08:46:37 -0600182 ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, NULL);
183 if (ret)
184 return log_msg_ret("stg", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600185 if (txtp)
186 *txtp = txt;
187
188 return txt->obj.id;
189}
190
191int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
192 const char *str, struct scene_obj_txt **txtp)
193{
194 struct scene_obj_txt *txt;
195 int ret;
196
Simon Glass0a4d14b2023-01-06 08:52:37 -0600197 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
198 sizeof(struct scene_obj_txt),
199 (struct scene_obj **)&txt);
200 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600201 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600202
Simon Glass9ef02aa2025-05-02 08:46:37 -0600203 ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, str);
204 if (ret)
205 return log_msg_ret("tsg", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600206 if (txtp)
207 *txtp = txt;
208
209 return txt->obj.id;
210}
211
212int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
213 uint font_size)
214{
215 struct scene_obj_txt *txt;
216
217 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
218 if (!txt)
219 return log_msg_ret("find", -ENOENT);
Simon Glass9ef02aa2025-05-02 08:46:37 -0600220 txt->gen.font_name = font_name;
221 txt->gen.font_size = font_size;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600222
223 return 0;
224}
225
226int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
227{
228 struct scene_obj *obj;
Simon Glassebec4972025-05-02 08:46:33 -0600229 int w, h;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600230
231 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
232 if (!obj)
233 return log_msg_ret("find", -ENOENT);
Simon Glassebec4972025-05-02 08:46:33 -0600234 w = obj->bbox.x1 - obj->bbox.x0;
235 h = obj->bbox.y1 - obj->bbox.y0;
Simon Glassbc3a15f2025-05-02 08:46:31 -0600236 obj->bbox.x0 = x;
237 obj->bbox.y0 = y;
Simon Glassebec4972025-05-02 08:46:33 -0600238 obj->bbox.x1 = obj->bbox.x0 + w;
239 obj->bbox.y1 = obj->bbox.y0 + h;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600240
241 return 0;
242}
243
Simon Glass7a960052023-06-01 10:22:52 -0600244int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
245{
246 struct scene_obj *obj;
247
248 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
249 if (!obj)
250 return log_msg_ret("find", -ENOENT);
Simon Glassebec4972025-05-02 08:46:33 -0600251 obj->bbox.x1 = obj->bbox.x0 + w;
252 obj->bbox.y1 = obj->bbox.y0 + h;
253 obj->flags |= SCENEOF_SIZE_VALID;
Simon Glass7a960052023-06-01 10:22:52 -0600254
255 return 0;
256}
257
Simon Glassc6143dc2025-05-02 08:46:35 -0600258int scene_obj_set_width(struct scene *scn, uint id, int w)
259{
260 struct scene_obj *obj;
261
262 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
263 if (!obj)
264 return log_msg_ret("find", -ENOENT);
265 obj->bbox.x1 = obj->bbox.x0 + w;
266
267 return 0;
268}
269
270int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1,
271 int y1)
272{
273 struct scene_obj *obj;
274
275 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
276 if (!obj)
277 return log_msg_ret("find", -ENOENT);
278 obj->bbox.x0 = x0;
279 obj->bbox.y0 = y0;
280 obj->bbox.x1 = x1;
281 obj->bbox.y1 = y1;
282 obj->flags |= SCENEOF_SIZE_VALID;
283
284 return 0;
285}
286
Simon Glass0a4d14b2023-01-06 08:52:37 -0600287int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
288{
Simon Glass6081b0f2023-06-01 10:22:50 -0600289 int ret;
290
291 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
292 hide ? SCENEOF_HIDE : 0);
293 if (ret)
294 return log_msg_ret("flg", ret);
295
296 return 0;
297}
298
299int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
300{
Simon Glass0a4d14b2023-01-06 08:52:37 -0600301 struct scene_obj *obj;
302
303 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
304 if (!obj)
305 return log_msg_ret("find", -ENOENT);
Simon Glass6081b0f2023-06-01 10:22:50 -0600306 obj->flags &= ~clr;
307 obj->flags |= set;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600308
309 return 0;
310}
311
312int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
313{
314 struct scene_obj *obj;
315
316 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
317 if (!obj)
318 return log_msg_ret("find", -ENOENT);
319
320 switch (obj->type) {
321 case SCENEOBJT_NONE:
322 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600323 case SCENEOBJT_TEXTLINE:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600324 break;
325 case SCENEOBJT_IMAGE: {
326 struct scene_obj_img *img = (struct scene_obj_img *)obj;
327 ulong width, height;
328 uint bpix;
329
330 video_bmp_get_info(img->data, &width, &height, &bpix);
331 if (widthp)
332 *widthp = width;
333 return height;
334 }
335 case SCENEOBJT_TEXT: {
Simon Glass9ef02aa2025-05-02 08:46:37 -0600336 struct scene_txt_generic *gen = &((struct scene_obj_txt *)obj)->gen;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600337 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600338 struct vidconsole_bbox bbox;
339 const char *str;
340 int len, ret;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600341
Simon Glass9ef02aa2025-05-02 08:46:37 -0600342 str = expo_get_str(exp, gen->str_id);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600343 if (!str)
344 return log_msg_ret("str", -ENOENT);
345 len = strlen(str);
346
347 /* if there is no console, make it up */
348 if (!exp->cons) {
349 if (widthp)
350 *widthp = 8 * len;
351 return 16;
352 }
353
Simon Glass9ef02aa2025-05-02 08:46:37 -0600354 ret = vidconsole_measure(scn->expo->cons, gen->font_name,
355 gen->font_size, str, -1, &bbox, NULL);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600356 if (ret)
357 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600358 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600359 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600360
Simon Glass9e1a86d2023-06-01 10:22:51 -0600361 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600362 }
363 }
364
365 return 0;
366}
367
368/**
Simon Glass118a7272023-10-01 19:13:30 -0600369 * scene_render_background() - Render the background for an object
370 *
371 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600372 * @box_only: true to show a box around the object, but keep the normal
373 * background colour inside
Simon Glass118a7272023-10-01 19:13:30 -0600374 */
Simon Glass0023d182023-10-01 19:13:34 -0600375static void scene_render_background(struct scene_obj *obj, bool box_only)
Simon Glass118a7272023-10-01 19:13:30 -0600376{
Simon Glass96910ef2025-05-02 08:46:34 -0600377 struct vidconsole_bbox bbox[SCENEBB_count], *sel;
Simon Glass118a7272023-10-01 19:13:30 -0600378 struct expo *exp = obj->scene->expo;
379 const struct expo_theme *theme = &exp->theme;
Simon Glass118a7272023-10-01 19:13:30 -0600380 struct udevice *dev = exp->display;
381 struct video_priv *vid_priv;
382 struct udevice *cons = exp->cons;
383 struct vidconsole_colour old;
384 enum colour_idx fore, back;
385 uint inset = theme->menu_inset;
386
Simon Glass21320da2025-04-02 06:29:33 +1300387 vid_priv = dev_get_uclass_priv(dev);
Simon Glass118a7272023-10-01 19:13:30 -0600388 /* draw a background for the object */
Simon Glass21320da2025-04-02 06:29:33 +1300389 if (vid_priv->white_on_black) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600390 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600391 back = VID_WHITE;
392 } else {
393 fore = VID_LIGHT_GRAY;
394 back = VID_BLACK;
395 }
396
397 /* see if this object wants to render a background */
Simon Glass96910ef2025-05-02 08:46:34 -0600398 if (scene_obj_calc_bbox(obj, bbox))
Simon Glass118a7272023-10-01 19:13:30 -0600399 return;
400
Simon Glass96910ef2025-05-02 08:46:34 -0600401 sel = &bbox[SCENEBB_label];
402 if (!sel->valid)
403 return;
404
Simon Glass118a7272023-10-01 19:13:30 -0600405 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass96910ef2025-05-02 08:46:34 -0600406 video_fill_part(dev, sel->x0 - inset, sel->y0 - inset,
407 sel->x1 + inset, sel->y1 + inset,
Simon Glass118a7272023-10-01 19:13:30 -0600408 vid_priv->colour_fg);
409 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600410 if (box_only) {
Simon Glass96910ef2025-05-02 08:46:34 -0600411 video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1,
Simon Glass0023d182023-10-01 19:13:34 -0600412 vid_priv->colour_bg);
413 }
Simon Glass118a7272023-10-01 19:13:30 -0600414}
415
Simon Glassa841d1a2025-05-02 08:46:38 -0600416static int scene_txt_render(struct expo *exp, struct udevice *dev,
417 struct udevice *cons, struct scene_obj *obj,
418 struct scene_txt_generic *gen, int x, int y,
419 int menu_inset)
420{
Simon Glass62f39d22025-05-02 08:46:39 -0600421 struct video_priv *vid_priv;
422 struct vidconsole_colour old;
423 enum colour_idx fore, back;
Simon Glassa841d1a2025-05-02 08:46:38 -0600424 const char *str;
425 int ret;
426
427 if (!cons)
428 return -ENOTSUPP;
429
430 if (gen->font_name || gen->font_size) {
431 ret = vidconsole_select_font(cons, gen->font_name,
432 gen->font_size);
433 } else {
434 ret = vidconsole_select_font(cons, NULL, 0);
435 }
436 if (ret && ret != -ENOSYS)
437 return log_msg_ret("font", ret);
438 str = expo_get_str(exp, gen->str_id);
Simon Glass62f39d22025-05-02 08:46:39 -0600439 if (!str)
440 return 0;
Simon Glassa841d1a2025-05-02 08:46:38 -0600441
Simon Glass62f39d22025-05-02 08:46:39 -0600442 vid_priv = dev_get_uclass_priv(dev);
443 if (vid_priv->white_on_black) {
444 fore = VID_BLACK;
445 back = VID_WHITE;
446 } else {
447 fore = VID_LIGHT_GRAY;
448 back = VID_BLACK;
449 }
Simon Glassa841d1a2025-05-02 08:46:38 -0600450
Simon Glass62f39d22025-05-02 08:46:39 -0600451 if (obj->flags & SCENEOF_POINT) {
452 vidconsole_push_colour(cons, fore, back, &old);
453 video_fill_part(dev, x - menu_inset, y, obj->bbox.x1,
454 obj->bbox.y1, vid_priv->colour_bg);
Simon Glassa841d1a2025-05-02 08:46:38 -0600455 }
Simon Glass62f39d22025-05-02 08:46:39 -0600456 vidconsole_set_cursor_pos(cons, x, y);
457 vidconsole_put_string(cons, str);
458 if (obj->flags & SCENEOF_POINT)
459 vidconsole_pop_colour(cons, &old);
Simon Glassa841d1a2025-05-02 08:46:38 -0600460
461 return 0;
462}
463
Simon Glass118a7272023-10-01 19:13:30 -0600464/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600465 * scene_obj_render() - Render an object
466 *
467 */
468static int scene_obj_render(struct scene_obj *obj, bool text_mode)
469{
470 struct scene *scn = obj->scene;
471 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600472 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600473 struct udevice *dev = exp->display;
474 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600475 int x, y, ret;
476
Simon Glassbc3a15f2025-05-02 08:46:31 -0600477 x = obj->bbox.x0;
478 y = obj->bbox.y0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600479
480 switch (obj->type) {
481 case SCENEOBJT_NONE:
482 break;
483 case SCENEOBJT_IMAGE: {
484 struct scene_obj_img *img = (struct scene_obj_img *)obj;
485
486 if (!cons)
487 return -ENOTSUPP;
488 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
489 true);
490 if (ret < 0)
491 return log_msg_ret("img", ret);
492 break;
493 }
494 case SCENEOBJT_TEXT: {
Simon Glassa841d1a2025-05-02 08:46:38 -0600495 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600496
Simon Glassa841d1a2025-05-02 08:46:38 -0600497 ret = scene_txt_render(exp, dev, cons, obj, &txt->gen, x, y,
498 theme->menu_inset);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600499 break;
500 }
501 case SCENEOBJT_MENU: {
502 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600503
504 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
505 if (!cons)
506 return -ENOTSUPP;
507
508 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600509 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600510 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600511 /*
512 * With a vidconsole, the text and item pointer are rendered as
513 * normal objects so we don't need to do anything here. The menu
514 * simply controls where they are positioned.
515 */
516 if (cons)
517 return -ENOTSUPP;
518
519 ret = scene_menu_display(menu);
520 if (ret < 0)
521 return log_msg_ret("img", ret);
522
523 break;
524 }
Simon Glass0023d182023-10-01 19:13:34 -0600525 case SCENEOBJT_TEXTLINE:
526 if (obj->flags & SCENEOF_OPEN)
527 scene_render_background(obj, true);
528 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600529 }
530
531 return 0;
532}
533
Simon Glass377f18e2024-10-14 16:31:55 -0600534int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
535{
536 struct scene_obj *obj;
537
538 arr->label_width = 0;
539 list_for_each_entry(obj, &scn->obj_head, sibling) {
540 uint label_id = 0;
541 int width;
542
543 switch (obj->type) {
544 case SCENEOBJT_NONE:
545 case SCENEOBJT_IMAGE:
546 case SCENEOBJT_TEXT:
547 break;
548 case SCENEOBJT_MENU: {
549 struct scene_obj_menu *menu;
550
551 menu = (struct scene_obj_menu *)obj,
552 label_id = menu->title_id;
553 break;
554 }
555 case SCENEOBJT_TEXTLINE: {
556 struct scene_obj_textline *tline;
557
558 tline = (struct scene_obj_textline *)obj,
559 label_id = tline->label_id;
560 break;
561 }
562 }
563
564 if (label_id) {
565 int ret;
566
567 ret = scene_obj_get_hw(scn, label_id, &width);
568 if (ret < 0)
569 return log_msg_ret("hei", ret);
570 arr->label_width = max(arr->label_width, width);
571 }
572 }
573
574 return 0;
575}
576
Simon Glass0a4d14b2023-01-06 08:52:37 -0600577int scene_arrange(struct scene *scn)
578{
Simon Glass377f18e2024-10-14 16:31:55 -0600579 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600580 struct scene_obj *obj;
581 int ret;
582
Simon Glass377f18e2024-10-14 16:31:55 -0600583 ret = scene_calc_arrange(scn, &arr);
584 if (ret < 0)
585 return log_msg_ret("arr", ret);
586
Simon Glass0a4d14b2023-01-06 08:52:37 -0600587 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600588 switch (obj->type) {
589 case SCENEOBJT_NONE:
590 case SCENEOBJT_IMAGE:
591 case SCENEOBJT_TEXT:
592 break;
593 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600594 struct scene_obj_menu *menu;
595
596 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600597 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600598 if (ret)
599 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600600 break;
601 }
Simon Glass0023d182023-10-01 19:13:34 -0600602 case SCENEOBJT_TEXTLINE: {
603 struct scene_obj_textline *tline;
604
605 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600606 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600607 if (ret)
608 return log_msg_ret("arr", ret);
609 break;
610 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600611 }
612 }
613
614 return 0;
615}
616
Simon Glass12f57732023-06-01 10:22:58 -0600617int scene_render_deps(struct scene *scn, uint id)
618{
619 struct scene_obj *obj;
620 int ret;
621
622 if (!id)
623 return 0;
624 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
625 if (!obj)
626 return log_msg_ret("obj", -ENOENT);
627
628 if (!(obj->flags & SCENEOF_HIDE)) {
629 ret = scene_obj_render(obj, false);
630 if (ret && ret != -ENOTSUPP)
631 return log_msg_ret("ren", ret);
632
Simon Glassb7a64532023-10-01 19:13:24 -0600633 switch (obj->type) {
634 case SCENEOBJT_NONE:
635 case SCENEOBJT_IMAGE:
636 case SCENEOBJT_TEXT:
637 break;
638 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600639 scene_menu_render_deps(scn,
640 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600641 break;
Simon Glass0023d182023-10-01 19:13:34 -0600642 case SCENEOBJT_TEXTLINE:
643 scene_textline_render_deps(scn,
644 (struct scene_obj_textline *)obj);
645 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600646 }
Simon Glass12f57732023-06-01 10:22:58 -0600647 }
648
649 return 0;
650}
651
Simon Glass0a4d14b2023-01-06 08:52:37 -0600652int scene_render(struct scene *scn)
653{
654 struct expo *exp = scn->expo;
655 struct scene_obj *obj;
656 int ret;
657
658 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600659 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600660 ret = scene_obj_render(obj, exp->text_mode);
661 if (ret && ret != -ENOTSUPP)
662 return log_msg_ret("ren", ret);
663 }
664 }
665
Simon Glass12f57732023-06-01 10:22:58 -0600666 /* render any highlighted object on top of the others */
667 if (scn->highlight_id && !exp->text_mode) {
668 ret = scene_render_deps(scn, scn->highlight_id);
669 if (ret && ret != -ENOTSUPP)
670 return log_msg_ret("dep", ret);
671 }
672
Simon Glass0a4d14b2023-01-06 08:52:37 -0600673 return 0;
674}
675
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600676/**
677 * send_key_obj() - Handle a keypress for moving between objects
678 *
679 * @scn: Scene to receive the key
680 * @key: Key to send (KEYCODE_UP)
681 * @event: Returns resulting event from this keypress
682 * Returns: 0 if OK, -ve on error
683 */
684static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
685 struct expo_action *event)
686{
687 switch (key) {
688 case BKEY_UP:
689 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
690 sibling)) {
691 obj = list_entry(obj->sibling.prev,
692 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600693 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600694 event->type = EXPOACT_POINT_OBJ;
695 event->select.id = obj->id;
696 log_debug("up to obj %d\n", event->select.id);
697 break;
698 }
699 }
700 break;
701 case BKEY_DOWN:
702 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
703 obj = list_entry(obj->sibling.next, struct scene_obj,
704 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600705 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600706 event->type = EXPOACT_POINT_OBJ;
707 event->select.id = obj->id;
708 log_debug("down to obj %d\n", event->select.id);
709 break;
710 }
711 }
712 break;
713 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600714 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600715 event->type = EXPOACT_OPEN;
716 event->select.id = obj->id;
717 log_debug("open obj %d\n", event->select.id);
718 }
719 break;
720 case BKEY_QUIT:
721 event->type = EXPOACT_QUIT;
722 log_debug("obj quit\n");
723 break;
724 }
725}
726
Simon Glass0a4d14b2023-01-06 08:52:37 -0600727int scene_send_key(struct scene *scn, int key, struct expo_action *event)
728{
729 struct scene_obj *obj;
730 int ret;
731
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600732 event->type = EXPOACT_NONE;
733
734 /*
735 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
736 * opened
737 */
738 if (scn->expo->popup) {
739 obj = NULL;
740 if (scn->highlight_id) {
741 obj = scene_obj_find(scn, scn->highlight_id,
742 SCENEOBJT_NONE);
743 }
744 if (!obj)
745 return 0;
746
747 if (!(obj->flags & SCENEOF_OPEN)) {
748 send_key_obj(scn, obj, key, event);
749 return 0;
750 }
751
Simon Glassb7a64532023-10-01 19:13:24 -0600752 switch (obj->type) {
753 case SCENEOBJT_NONE:
754 case SCENEOBJT_IMAGE:
755 case SCENEOBJT_TEXT:
756 break;
757 case SCENEOBJT_MENU: {
758 struct scene_obj_menu *menu;
759
760 menu = (struct scene_obj_menu *)obj,
761 ret = scene_menu_send_key(scn, menu, key, event);
762 if (ret)
763 return log_msg_ret("key", ret);
764 break;
765 }
Simon Glass0023d182023-10-01 19:13:34 -0600766 case SCENEOBJT_TEXTLINE: {
767 struct scene_obj_textline *tline;
768
769 tline = (struct scene_obj_textline *)obj,
770 ret = scene_textline_send_key(scn, tline, key, event);
771 if (ret)
772 return log_msg_ret("key", ret);
773 break;
774 }
Simon Glassb7a64532023-10-01 19:13:24 -0600775 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600776 return 0;
777 }
778
Simon Glass0a4d14b2023-01-06 08:52:37 -0600779 list_for_each_entry(obj, &scn->obj_head, sibling) {
780 if (obj->type == SCENEOBJT_MENU) {
781 struct scene_obj_menu *menu;
782
783 menu = (struct scene_obj_menu *)obj,
784 ret = scene_menu_send_key(scn, menu, key, event);
785 if (ret)
786 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600787 break;
788 }
789 }
790
791 return 0;
792}
Simon Glass7a960052023-06-01 10:22:52 -0600793
Simon Glass96910ef2025-05-02 08:46:34 -0600794int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
Simon Glassf0994692023-10-01 19:13:29 -0600795{
796 switch (obj->type) {
797 case SCENEOBJT_NONE:
798 case SCENEOBJT_IMAGE:
799 case SCENEOBJT_TEXT:
800 return -ENOSYS;
801 case SCENEOBJT_MENU: {
802 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
803
Simon Glass96910ef2025-05-02 08:46:34 -0600804 scene_menu_calc_bbox(menu, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600805 break;
806 }
Simon Glass0023d182023-10-01 19:13:34 -0600807 case SCENEOBJT_TEXTLINE: {
808 struct scene_obj_textline *tline;
809
810 tline = (struct scene_obj_textline *)obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600811 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
812 &bbox[SCENEBB_label]);
Simon Glass0023d182023-10-01 19:13:34 -0600813 break;
814 }
Simon Glassf0994692023-10-01 19:13:29 -0600815 }
816
817 return 0;
818}
819
Simon Glass7a960052023-06-01 10:22:52 -0600820int scene_calc_dims(struct scene *scn, bool do_menus)
821{
822 struct scene_obj *obj;
823 int ret;
824
825 list_for_each_entry(obj, &scn->obj_head, sibling) {
826 switch (obj->type) {
827 case SCENEOBJT_NONE:
828 case SCENEOBJT_TEXT:
829 case SCENEOBJT_IMAGE: {
830 int width;
831
832 if (!do_menus) {
833 ret = scene_obj_get_hw(scn, obj->id, &width);
834 if (ret < 0)
835 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -0600836 obj->dims.x = width;
837 obj->dims.y = ret;
838 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
839 obj->bbox.x1 = obj->bbox.x0 + width;
840 obj->bbox.y1 = obj->bbox.y0 + ret;
841 obj->flags |= SCENEOF_SIZE_VALID;
842 }
Simon Glass7a960052023-06-01 10:22:52 -0600843 }
844 break;
845 }
846 case SCENEOBJT_MENU: {
847 struct scene_obj_menu *menu;
848
849 if (do_menus) {
850 menu = (struct scene_obj_menu *)obj;
851
852 ret = scene_menu_calc_dims(menu);
853 if (ret)
854 return log_msg_ret("men", ret);
855 }
856 break;
857 }
Simon Glass0023d182023-10-01 19:13:34 -0600858 case SCENEOBJT_TEXTLINE: {
859 struct scene_obj_textline *tline;
860
861 tline = (struct scene_obj_textline *)obj;
862 ret = scene_textline_calc_dims(tline);
863 if (ret)
864 return log_msg_ret("men", ret);
865
866 break;
867 }
Simon Glass7a960052023-06-01 10:22:52 -0600868 }
869 }
870
871 return 0;
872}
Simon Glassc999e172023-06-01 10:22:53 -0600873
874int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
875{
876 struct scene_obj *obj;
877 int ret;
878
879 /* Avoid error-checking optional items */
880 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
881
882 list_for_each_entry(obj, &scn->obj_head, sibling) {
883 switch (obj->type) {
884 case SCENEOBJT_NONE:
885 case SCENEOBJT_IMAGE:
886 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600887 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600888 break;
889 case SCENEOBJT_TEXT:
890 scene_txt_set_font(scn, obj->id, NULL,
891 theme->font_size);
892 break;
893 }
894 }
895
896 ret = scene_arrange(scn);
897 if (ret)
898 return log_msg_ret("arr", ret);
899
900 return 0;
901}
Simon Glass01922ec2023-06-01 10:22:57 -0600902
903void scene_set_highlight_id(struct scene *scn, uint id)
904{
905 scn->highlight_id = id;
906}
907
908void scene_highlight_first(struct scene *scn)
909{
910 struct scene_obj *obj;
911
912 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600913 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600914 scene_set_highlight_id(scn, obj->id);
915 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600916 }
917 }
918}
919
Simon Glassf6a943a2023-10-01 19:13:33 -0600920static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
921{
922 int ret;
923
924 switch (obj->type) {
925 case SCENEOBJT_NONE:
926 case SCENEOBJT_IMAGE:
927 case SCENEOBJT_MENU:
928 case SCENEOBJT_TEXT:
929 break;
930 case SCENEOBJT_TEXTLINE:
931 ret = scene_textline_open(scn,
932 (struct scene_obj_textline *)obj);
933 if (ret)
934 return log_msg_ret("op", ret);
935 break;
936 }
937
938 return 0;
939}
940
Simon Glass01922ec2023-06-01 10:22:57 -0600941int scene_set_open(struct scene *scn, uint id, bool open)
942{
Simon Glassf6a943a2023-10-01 19:13:33 -0600943 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600944 int ret;
945
Simon Glassf6a943a2023-10-01 19:13:33 -0600946 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
947 if (!obj)
948 return log_msg_ret("find", -ENOENT);
949
950 if (open) {
951 ret = scene_obj_open(scn, obj);
952 if (ret)
953 return log_msg_ret("op", ret);
954 }
955
Simon Glass01922ec2023-06-01 10:22:57 -0600956 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
957 open ? SCENEOF_OPEN : 0);
958 if (ret)
959 return log_msg_ret("flg", ret);
960
961 return 0;
962}
Simon Glasse90acd82023-08-14 16:40:23 -0600963
964int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
965 void *priv)
966{
967 struct scene_obj *obj;
968
969 list_for_each_entry(obj, &scn->obj_head, sibling) {
970 int ret;
971
972 ret = iter(obj, priv);
973 if (ret)
974 return log_msg_ret("itr", ret);
975 }
976
977 return 0;
978}
Simon Glassf0994692023-10-01 19:13:29 -0600979
Simon Glass96910ef2025-05-02 08:46:34 -0600980int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
981 struct vidconsole_bbox *dst)
982{
983 if (dst->valid) {
984 dst->x0 = min(dst->x0, src->x0 - inset);
985 dst->y0 = min(dst->y0, src->y0);
986 dst->x1 = max(dst->x1, src->x1 + inset);
987 dst->y1 = max(dst->y1, src->y1);
988 } else {
989 dst->x0 = src->x0 - inset;
990 dst->y0 = src->y0;
991 dst->x1 = src->x1 + inset;
992 dst->y1 = src->y1;
993 dst->valid = true;
994 }
995
996 return 0;
997}
998
Simon Glassf0994692023-10-01 19:13:29 -0600999int scene_bbox_union(struct scene *scn, uint id, int inset,
1000 struct vidconsole_bbox *bbox)
1001{
1002 struct scene_obj *obj;
Simon Glass96910ef2025-05-02 08:46:34 -06001003 struct vidconsole_bbox local;
Simon Glassf0994692023-10-01 19:13:29 -06001004
1005 if (!id)
1006 return 0;
1007 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1008 if (!obj)
1009 return log_msg_ret("obj", -ENOENT);
Simon Glass96910ef2025-05-02 08:46:34 -06001010 local.x0 = obj->bbox.x0;
1011 local.y0 = obj->bbox.y0;
1012 local.x1 = obj->bbox.x1;
1013 local.y1 = obj->bbox.y1;
1014 local.valid = true;
1015 scene_bbox_join(&local, inset, bbox);
Simon Glassf0994692023-10-01 19:13:29 -06001016
1017 return 0;
1018}