blob: da3d30a9cab5281fba80c2283a4c6ba36e1b290d [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{
421 const char *str;
422 int ret;
423
424 if (!cons)
425 return -ENOTSUPP;
426
427 if (gen->font_name || gen->font_size) {
428 ret = vidconsole_select_font(cons, gen->font_name,
429 gen->font_size);
430 } else {
431 ret = vidconsole_select_font(cons, NULL, 0);
432 }
433 if (ret && ret != -ENOSYS)
434 return log_msg_ret("font", ret);
435 str = expo_get_str(exp, gen->str_id);
436 if (str) {
437 struct video_priv *vid_priv;
438 struct vidconsole_colour old;
439 enum colour_idx fore, back;
440
441 vid_priv = dev_get_uclass_priv(dev);
442 if (vid_priv->white_on_black) {
443 fore = VID_BLACK;
444 back = VID_WHITE;
445 } else {
446 fore = VID_LIGHT_GRAY;
447 back = VID_BLACK;
448 }
449
450 if (obj->flags & SCENEOF_POINT) {
451 vidconsole_push_colour(cons, fore, back, &old);
452 video_fill_part(dev, x - menu_inset, y,
453 obj->bbox.x1,
454 obj->bbox.y1,
455 vid_priv->colour_bg);
456 }
457 vidconsole_set_cursor_pos(cons, x, y);
458 vidconsole_put_string(cons, str);
459 if (obj->flags & SCENEOF_POINT)
460 vidconsole_pop_colour(cons, &old);
461 }
462
463 return 0;
464}
465
Simon Glass118a7272023-10-01 19:13:30 -0600466/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600467 * scene_obj_render() - Render an object
468 *
469 */
470static int scene_obj_render(struct scene_obj *obj, bool text_mode)
471{
472 struct scene *scn = obj->scene;
473 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600474 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600475 struct udevice *dev = exp->display;
476 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600477 int x, y, ret;
478
Simon Glassbc3a15f2025-05-02 08:46:31 -0600479 x = obj->bbox.x0;
480 y = obj->bbox.y0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600481
482 switch (obj->type) {
483 case SCENEOBJT_NONE:
484 break;
485 case SCENEOBJT_IMAGE: {
486 struct scene_obj_img *img = (struct scene_obj_img *)obj;
487
488 if (!cons)
489 return -ENOTSUPP;
490 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
491 true);
492 if (ret < 0)
493 return log_msg_ret("img", ret);
494 break;
495 }
496 case SCENEOBJT_TEXT: {
Simon Glassa841d1a2025-05-02 08:46:38 -0600497 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600498
Simon Glassa841d1a2025-05-02 08:46:38 -0600499 ret = scene_txt_render(exp, dev, cons, obj, &txt->gen, x, y,
500 theme->menu_inset);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600501 break;
502 }
503 case SCENEOBJT_MENU: {
504 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600505
506 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
507 if (!cons)
508 return -ENOTSUPP;
509
510 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600511 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600512 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600513 /*
514 * With a vidconsole, the text and item pointer are rendered as
515 * normal objects so we don't need to do anything here. The menu
516 * simply controls where they are positioned.
517 */
518 if (cons)
519 return -ENOTSUPP;
520
521 ret = scene_menu_display(menu);
522 if (ret < 0)
523 return log_msg_ret("img", ret);
524
525 break;
526 }
Simon Glass0023d182023-10-01 19:13:34 -0600527 case SCENEOBJT_TEXTLINE:
528 if (obj->flags & SCENEOF_OPEN)
529 scene_render_background(obj, true);
530 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600531 }
532
533 return 0;
534}
535
Simon Glass377f18e2024-10-14 16:31:55 -0600536int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
537{
538 struct scene_obj *obj;
539
540 arr->label_width = 0;
541 list_for_each_entry(obj, &scn->obj_head, sibling) {
542 uint label_id = 0;
543 int width;
544
545 switch (obj->type) {
546 case SCENEOBJT_NONE:
547 case SCENEOBJT_IMAGE:
548 case SCENEOBJT_TEXT:
549 break;
550 case SCENEOBJT_MENU: {
551 struct scene_obj_menu *menu;
552
553 menu = (struct scene_obj_menu *)obj,
554 label_id = menu->title_id;
555 break;
556 }
557 case SCENEOBJT_TEXTLINE: {
558 struct scene_obj_textline *tline;
559
560 tline = (struct scene_obj_textline *)obj,
561 label_id = tline->label_id;
562 break;
563 }
564 }
565
566 if (label_id) {
567 int ret;
568
569 ret = scene_obj_get_hw(scn, label_id, &width);
570 if (ret < 0)
571 return log_msg_ret("hei", ret);
572 arr->label_width = max(arr->label_width, width);
573 }
574 }
575
576 return 0;
577}
578
Simon Glass0a4d14b2023-01-06 08:52:37 -0600579int scene_arrange(struct scene *scn)
580{
Simon Glass377f18e2024-10-14 16:31:55 -0600581 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600582 struct scene_obj *obj;
583 int ret;
584
Simon Glass377f18e2024-10-14 16:31:55 -0600585 ret = scene_calc_arrange(scn, &arr);
586 if (ret < 0)
587 return log_msg_ret("arr", ret);
588
Simon Glass0a4d14b2023-01-06 08:52:37 -0600589 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600590 switch (obj->type) {
591 case SCENEOBJT_NONE:
592 case SCENEOBJT_IMAGE:
593 case SCENEOBJT_TEXT:
594 break;
595 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600596 struct scene_obj_menu *menu;
597
598 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600599 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600600 if (ret)
601 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600602 break;
603 }
Simon Glass0023d182023-10-01 19:13:34 -0600604 case SCENEOBJT_TEXTLINE: {
605 struct scene_obj_textline *tline;
606
607 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600608 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600609 if (ret)
610 return log_msg_ret("arr", ret);
611 break;
612 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600613 }
614 }
615
616 return 0;
617}
618
Simon Glass12f57732023-06-01 10:22:58 -0600619int scene_render_deps(struct scene *scn, uint id)
620{
621 struct scene_obj *obj;
622 int ret;
623
624 if (!id)
625 return 0;
626 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
627 if (!obj)
628 return log_msg_ret("obj", -ENOENT);
629
630 if (!(obj->flags & SCENEOF_HIDE)) {
631 ret = scene_obj_render(obj, false);
632 if (ret && ret != -ENOTSUPP)
633 return log_msg_ret("ren", ret);
634
Simon Glassb7a64532023-10-01 19:13:24 -0600635 switch (obj->type) {
636 case SCENEOBJT_NONE:
637 case SCENEOBJT_IMAGE:
638 case SCENEOBJT_TEXT:
639 break;
640 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600641 scene_menu_render_deps(scn,
642 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600643 break;
Simon Glass0023d182023-10-01 19:13:34 -0600644 case SCENEOBJT_TEXTLINE:
645 scene_textline_render_deps(scn,
646 (struct scene_obj_textline *)obj);
647 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600648 }
Simon Glass12f57732023-06-01 10:22:58 -0600649 }
650
651 return 0;
652}
653
Simon Glass0a4d14b2023-01-06 08:52:37 -0600654int scene_render(struct scene *scn)
655{
656 struct expo *exp = scn->expo;
657 struct scene_obj *obj;
658 int ret;
659
660 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600661 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600662 ret = scene_obj_render(obj, exp->text_mode);
663 if (ret && ret != -ENOTSUPP)
664 return log_msg_ret("ren", ret);
665 }
666 }
667
Simon Glass12f57732023-06-01 10:22:58 -0600668 /* render any highlighted object on top of the others */
669 if (scn->highlight_id && !exp->text_mode) {
670 ret = scene_render_deps(scn, scn->highlight_id);
671 if (ret && ret != -ENOTSUPP)
672 return log_msg_ret("dep", ret);
673 }
674
Simon Glass0a4d14b2023-01-06 08:52:37 -0600675 return 0;
676}
677
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600678/**
679 * send_key_obj() - Handle a keypress for moving between objects
680 *
681 * @scn: Scene to receive the key
682 * @key: Key to send (KEYCODE_UP)
683 * @event: Returns resulting event from this keypress
684 * Returns: 0 if OK, -ve on error
685 */
686static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
687 struct expo_action *event)
688{
689 switch (key) {
690 case BKEY_UP:
691 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
692 sibling)) {
693 obj = list_entry(obj->sibling.prev,
694 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600695 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600696 event->type = EXPOACT_POINT_OBJ;
697 event->select.id = obj->id;
698 log_debug("up to obj %d\n", event->select.id);
699 break;
700 }
701 }
702 break;
703 case BKEY_DOWN:
704 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
705 obj = list_entry(obj->sibling.next, struct scene_obj,
706 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600707 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600708 event->type = EXPOACT_POINT_OBJ;
709 event->select.id = obj->id;
710 log_debug("down to obj %d\n", event->select.id);
711 break;
712 }
713 }
714 break;
715 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600716 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600717 event->type = EXPOACT_OPEN;
718 event->select.id = obj->id;
719 log_debug("open obj %d\n", event->select.id);
720 }
721 break;
722 case BKEY_QUIT:
723 event->type = EXPOACT_QUIT;
724 log_debug("obj quit\n");
725 break;
726 }
727}
728
Simon Glass0a4d14b2023-01-06 08:52:37 -0600729int scene_send_key(struct scene *scn, int key, struct expo_action *event)
730{
731 struct scene_obj *obj;
732 int ret;
733
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600734 event->type = EXPOACT_NONE;
735
736 /*
737 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
738 * opened
739 */
740 if (scn->expo->popup) {
741 obj = NULL;
742 if (scn->highlight_id) {
743 obj = scene_obj_find(scn, scn->highlight_id,
744 SCENEOBJT_NONE);
745 }
746 if (!obj)
747 return 0;
748
749 if (!(obj->flags & SCENEOF_OPEN)) {
750 send_key_obj(scn, obj, key, event);
751 return 0;
752 }
753
Simon Glassb7a64532023-10-01 19:13:24 -0600754 switch (obj->type) {
755 case SCENEOBJT_NONE:
756 case SCENEOBJT_IMAGE:
757 case SCENEOBJT_TEXT:
758 break;
759 case SCENEOBJT_MENU: {
760 struct scene_obj_menu *menu;
761
762 menu = (struct scene_obj_menu *)obj,
763 ret = scene_menu_send_key(scn, menu, key, event);
764 if (ret)
765 return log_msg_ret("key", ret);
766 break;
767 }
Simon Glass0023d182023-10-01 19:13:34 -0600768 case SCENEOBJT_TEXTLINE: {
769 struct scene_obj_textline *tline;
770
771 tline = (struct scene_obj_textline *)obj,
772 ret = scene_textline_send_key(scn, tline, key, event);
773 if (ret)
774 return log_msg_ret("key", ret);
775 break;
776 }
Simon Glassb7a64532023-10-01 19:13:24 -0600777 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600778 return 0;
779 }
780
Simon Glass0a4d14b2023-01-06 08:52:37 -0600781 list_for_each_entry(obj, &scn->obj_head, sibling) {
782 if (obj->type == SCENEOBJT_MENU) {
783 struct scene_obj_menu *menu;
784
785 menu = (struct scene_obj_menu *)obj,
786 ret = scene_menu_send_key(scn, menu, key, event);
787 if (ret)
788 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600789 break;
790 }
791 }
792
793 return 0;
794}
Simon Glass7a960052023-06-01 10:22:52 -0600795
Simon Glass96910ef2025-05-02 08:46:34 -0600796int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
Simon Glassf0994692023-10-01 19:13:29 -0600797{
798 switch (obj->type) {
799 case SCENEOBJT_NONE:
800 case SCENEOBJT_IMAGE:
801 case SCENEOBJT_TEXT:
802 return -ENOSYS;
803 case SCENEOBJT_MENU: {
804 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
805
Simon Glass96910ef2025-05-02 08:46:34 -0600806 scene_menu_calc_bbox(menu, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600807 break;
808 }
Simon Glass0023d182023-10-01 19:13:34 -0600809 case SCENEOBJT_TEXTLINE: {
810 struct scene_obj_textline *tline;
811
812 tline = (struct scene_obj_textline *)obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600813 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
814 &bbox[SCENEBB_label]);
Simon Glass0023d182023-10-01 19:13:34 -0600815 break;
816 }
Simon Glassf0994692023-10-01 19:13:29 -0600817 }
818
819 return 0;
820}
821
Simon Glass7a960052023-06-01 10:22:52 -0600822int scene_calc_dims(struct scene *scn, bool do_menus)
823{
824 struct scene_obj *obj;
825 int ret;
826
827 list_for_each_entry(obj, &scn->obj_head, sibling) {
828 switch (obj->type) {
829 case SCENEOBJT_NONE:
830 case SCENEOBJT_TEXT:
831 case SCENEOBJT_IMAGE: {
832 int width;
833
834 if (!do_menus) {
835 ret = scene_obj_get_hw(scn, obj->id, &width);
836 if (ret < 0)
837 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -0600838 obj->dims.x = width;
839 obj->dims.y = ret;
840 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
841 obj->bbox.x1 = obj->bbox.x0 + width;
842 obj->bbox.y1 = obj->bbox.y0 + ret;
843 obj->flags |= SCENEOF_SIZE_VALID;
844 }
Simon Glass7a960052023-06-01 10:22:52 -0600845 }
846 break;
847 }
848 case SCENEOBJT_MENU: {
849 struct scene_obj_menu *menu;
850
851 if (do_menus) {
852 menu = (struct scene_obj_menu *)obj;
853
854 ret = scene_menu_calc_dims(menu);
855 if (ret)
856 return log_msg_ret("men", ret);
857 }
858 break;
859 }
Simon Glass0023d182023-10-01 19:13:34 -0600860 case SCENEOBJT_TEXTLINE: {
861 struct scene_obj_textline *tline;
862
863 tline = (struct scene_obj_textline *)obj;
864 ret = scene_textline_calc_dims(tline);
865 if (ret)
866 return log_msg_ret("men", ret);
867
868 break;
869 }
Simon Glass7a960052023-06-01 10:22:52 -0600870 }
871 }
872
873 return 0;
874}
Simon Glassc999e172023-06-01 10:22:53 -0600875
876int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
877{
878 struct scene_obj *obj;
879 int ret;
880
881 /* Avoid error-checking optional items */
882 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
883
884 list_for_each_entry(obj, &scn->obj_head, sibling) {
885 switch (obj->type) {
886 case SCENEOBJT_NONE:
887 case SCENEOBJT_IMAGE:
888 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600889 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600890 break;
891 case SCENEOBJT_TEXT:
892 scene_txt_set_font(scn, obj->id, NULL,
893 theme->font_size);
894 break;
895 }
896 }
897
898 ret = scene_arrange(scn);
899 if (ret)
900 return log_msg_ret("arr", ret);
901
902 return 0;
903}
Simon Glass01922ec2023-06-01 10:22:57 -0600904
905void scene_set_highlight_id(struct scene *scn, uint id)
906{
907 scn->highlight_id = id;
908}
909
910void scene_highlight_first(struct scene *scn)
911{
912 struct scene_obj *obj;
913
914 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600915 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600916 scene_set_highlight_id(scn, obj->id);
917 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600918 }
919 }
920}
921
Simon Glassf6a943a2023-10-01 19:13:33 -0600922static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
923{
924 int ret;
925
926 switch (obj->type) {
927 case SCENEOBJT_NONE:
928 case SCENEOBJT_IMAGE:
929 case SCENEOBJT_MENU:
930 case SCENEOBJT_TEXT:
931 break;
932 case SCENEOBJT_TEXTLINE:
933 ret = scene_textline_open(scn,
934 (struct scene_obj_textline *)obj);
935 if (ret)
936 return log_msg_ret("op", ret);
937 break;
938 }
939
940 return 0;
941}
942
Simon Glass01922ec2023-06-01 10:22:57 -0600943int scene_set_open(struct scene *scn, uint id, bool open)
944{
Simon Glassf6a943a2023-10-01 19:13:33 -0600945 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600946 int ret;
947
Simon Glassf6a943a2023-10-01 19:13:33 -0600948 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
949 if (!obj)
950 return log_msg_ret("find", -ENOENT);
951
952 if (open) {
953 ret = scene_obj_open(scn, obj);
954 if (ret)
955 return log_msg_ret("op", ret);
956 }
957
Simon Glass01922ec2023-06-01 10:22:57 -0600958 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
959 open ? SCENEOF_OPEN : 0);
960 if (ret)
961 return log_msg_ret("flg", ret);
962
963 return 0;
964}
Simon Glasse90acd82023-08-14 16:40:23 -0600965
966int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
967 void *priv)
968{
969 struct scene_obj *obj;
970
971 list_for_each_entry(obj, &scn->obj_head, sibling) {
972 int ret;
973
974 ret = iter(obj, priv);
975 if (ret)
976 return log_msg_ret("itr", ret);
977 }
978
979 return 0;
980}
Simon Glassf0994692023-10-01 19:13:29 -0600981
Simon Glass96910ef2025-05-02 08:46:34 -0600982int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
983 struct vidconsole_bbox *dst)
984{
985 if (dst->valid) {
986 dst->x0 = min(dst->x0, src->x0 - inset);
987 dst->y0 = min(dst->y0, src->y0);
988 dst->x1 = max(dst->x1, src->x1 + inset);
989 dst->y1 = max(dst->y1, src->y1);
990 } else {
991 dst->x0 = src->x0 - inset;
992 dst->y0 = src->y0;
993 dst->x1 = src->x1 + inset;
994 dst->y1 = src->y1;
995 dst->valid = true;
996 }
997
998 return 0;
999}
1000
Simon Glassf0994692023-10-01 19:13:29 -06001001int scene_bbox_union(struct scene *scn, uint id, int inset,
1002 struct vidconsole_bbox *bbox)
1003{
1004 struct scene_obj *obj;
Simon Glass96910ef2025-05-02 08:46:34 -06001005 struct vidconsole_bbox local;
Simon Glassf0994692023-10-01 19:13:29 -06001006
1007 if (!id)
1008 return 0;
1009 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1010 if (!obj)
1011 return log_msg_ret("obj", -ENOENT);
Simon Glass96910ef2025-05-02 08:46:34 -06001012 local.x0 = obj->bbox.x0;
1013 local.y0 = obj->bbox.y0;
1014 local.x1 = obj->bbox.x1;
1015 local.y1 = obj->bbox.y1;
1016 local.valid = true;
1017 scene_bbox_join(&local, inset, bbox);
Simon Glassf0994692023-10-01 19:13:29 -06001018
1019 return 0;
1020}