blob: 72a8e27a93d7a7e133bdac3d75dc5d00d677bada [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 Glasscfb4f2c2025-05-02 08:46:42 -060011#include <alist.h>
Simon Glass0a4d14b2023-01-06 08:52:37 -060012#include <dm.h>
13#include <expo.h>
14#include <malloc.h>
15#include <mapmem.h>
Simon Glassf0e1e8c2023-06-01 10:22:59 -060016#include <menu.h>
Simon Glass0a4d14b2023-01-06 08:52:37 -060017#include <video.h>
18#include <video_console.h>
19#include <linux/input.h>
20#include "scene_internal.h"
21
Simon Glass0a4d14b2023-01-06 08:52:37 -060022int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
23{
24 struct scene *scn;
25
26 scn = calloc(1, sizeof(struct scene));
27 if (!scn)
28 return log_msg_ret("expo", -ENOMEM);
29 scn->name = strdup(name);
30 if (!scn->name) {
31 free(scn);
32 return log_msg_ret("name", -ENOMEM);
33 }
34
Simon Glass6651e942025-05-01 07:37:01 -060035 if (!abuf_init_size(&scn->buf, EXPO_MAX_CHARS + 1)) {
Simon Glassa968f5f2023-10-01 19:13:31 -060036 free(scn->name);
37 free(scn);
38 return log_msg_ret("buf", -ENOMEM);
39 }
40 abuf_init(&scn->entry_save);
41
Simon Glass0a4d14b2023-01-06 08:52:37 -060042 INIT_LIST_HEAD(&scn->obj_head);
43 scn->id = resolve_id(exp, id);
44 scn->expo = exp;
45 list_add_tail(&scn->sibling, &exp->scene_head);
46
47 *scnp = scn;
48
49 return scn->id;
50}
51
52void scene_obj_destroy(struct scene_obj *obj)
53{
54 if (obj->type == SCENEOBJT_MENU)
55 scene_menu_destroy((struct scene_obj_menu *)obj);
56 free(obj->name);
57 free(obj);
58}
59
60void scene_destroy(struct scene *scn)
61{
62 struct scene_obj *obj, *next;
63
64 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
65 scene_obj_destroy(obj);
66
Simon Glassa968f5f2023-10-01 19:13:31 -060067 abuf_uninit(&scn->entry_save);
68 abuf_uninit(&scn->buf);
Simon Glass0a4d14b2023-01-06 08:52:37 -060069 free(scn->name);
Simon Glass0a4d14b2023-01-06 08:52:37 -060070 free(scn);
71}
72
Simon Glass0a4d14b2023-01-06 08:52:37 -060073int scene_obj_count(struct scene *scn)
74{
Sughosh Ganuebb1c202024-08-28 22:24:22 +053075 return list_count_nodes(&scn->obj_head);
Simon Glass0a4d14b2023-01-06 08:52:37 -060076}
77
Simon Glass45ff0bc2023-08-14 16:40:21 -060078void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
Simon Glass0a4d14b2023-01-06 08:52:37 -060079{
80 struct scene_obj *obj;
81
82 list_for_each_entry(obj, &scn->obj_head, sibling) {
83 if (obj->id == id &&
84 (type == SCENEOBJT_NONE || obj->type == type))
85 return obj;
86 }
87
88 return NULL;
89}
90
Simon Glassc8925112023-06-01 10:23:02 -060091void *scene_obj_find_by_name(struct scene *scn, const char *name)
92{
93 struct scene_obj *obj;
94
95 list_for_each_entry(obj, &scn->obj_head, sibling) {
96 if (!strcmp(name, obj->name))
97 return obj;
98 }
99
100 return NULL;
101}
102
Simon Glass0a4d14b2023-01-06 08:52:37 -0600103int scene_obj_add(struct scene *scn, const char *name, uint id,
104 enum scene_obj_t type, uint size, struct scene_obj **objp)
105{
106 struct scene_obj *obj;
107
108 obj = calloc(1, size);
109 if (!obj)
110 return log_msg_ret("obj", -ENOMEM);
111 obj->name = strdup(name);
112 if (!obj->name) {
113 free(obj);
114 return log_msg_ret("name", -ENOMEM);
115 }
116
117 obj->id = resolve_id(scn->expo, id);
118 obj->scene = scn;
119 obj->type = type;
120 list_add_tail(&obj->sibling, &scn->obj_head);
121 *objp = obj;
122
123 return obj->id;
124}
125
126int scene_img(struct scene *scn, const char *name, uint id, char *data,
127 struct scene_obj_img **imgp)
128{
129 struct scene_obj_img *img;
130 int ret;
131
132 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
133 sizeof(struct scene_obj_img),
134 (struct scene_obj **)&img);
135 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600136 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600137
138 img->data = data;
139
140 if (imgp)
141 *imgp = img;
142
143 return img->obj.id;
144}
145
Simon Glass9ef02aa2025-05-02 08:46:37 -0600146int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen,
147 const char *name, uint str_id, const char *str)
148{
149 int ret;
150
151 if (str) {
152 ret = expo_str(exp, name, str_id, str);
153 if (ret < 0)
154 return log_msg_ret("str", ret);
155 if (str_id && ret != str_id)
156 return log_msg_ret("id", -EEXIST);
157 str_id = ret;
158 } else {
159 ret = resolve_id(exp, str_id);
160 if (ret < 0)
161 return log_msg_ret("nst", ret);
162 if (str_id && ret != str_id)
163 return log_msg_ret("nid", -EEXIST);
164 }
165
166 gen->str_id = str_id;
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600167 alist_init_struct(&gen->lines, struct vidconsole_mline);
Simon Glass9ef02aa2025-05-02 08:46:37 -0600168
169 return 0;
170}
171
Simon Glass0a4d14b2023-01-06 08:52:37 -0600172int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
173 struct scene_obj_txt **txtp)
174{
175 struct scene_obj_txt *txt;
176 int ret;
177
178 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
179 sizeof(struct scene_obj_txt),
180 (struct scene_obj **)&txt);
181 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600182 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600183
Simon Glass9ef02aa2025-05-02 08:46:37 -0600184 ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, NULL);
185 if (ret)
186 return log_msg_ret("stg", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600187 if (txtp)
188 *txtp = txt;
189
190 return txt->obj.id;
191}
192
193int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
194 const char *str, struct scene_obj_txt **txtp)
195{
196 struct scene_obj_txt *txt;
197 int ret;
198
Simon Glass0a4d14b2023-01-06 08:52:37 -0600199 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
200 sizeof(struct scene_obj_txt),
201 (struct scene_obj **)&txt);
202 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600203 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600204
Simon Glass9ef02aa2025-05-02 08:46:37 -0600205 ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, str);
206 if (ret)
207 return log_msg_ret("tsg", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600208 if (txtp)
209 *txtp = txt;
210
211 return txt->obj.id;
212}
213
214int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
215 uint font_size)
216{
217 struct scene_obj_txt *txt;
218
219 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
220 if (!txt)
221 return log_msg_ret("find", -ENOENT);
Simon Glass9ef02aa2025-05-02 08:46:37 -0600222 txt->gen.font_name = font_name;
223 txt->gen.font_size = font_size;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600224
225 return 0;
226}
227
228int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
229{
230 struct scene_obj *obj;
Simon Glassebec4972025-05-02 08:46:33 -0600231 int w, h;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600232
233 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
234 if (!obj)
235 return log_msg_ret("find", -ENOENT);
Simon Glassebec4972025-05-02 08:46:33 -0600236 w = obj->bbox.x1 - obj->bbox.x0;
237 h = obj->bbox.y1 - obj->bbox.y0;
Simon Glassbc3a15f2025-05-02 08:46:31 -0600238 obj->bbox.x0 = x;
239 obj->bbox.y0 = y;
Simon Glassebec4972025-05-02 08:46:33 -0600240 obj->bbox.x1 = obj->bbox.x0 + w;
241 obj->bbox.y1 = obj->bbox.y0 + h;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600242
243 return 0;
244}
245
Simon Glass7a960052023-06-01 10:22:52 -0600246int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
247{
248 struct scene_obj *obj;
249
250 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
251 if (!obj)
252 return log_msg_ret("find", -ENOENT);
Simon Glassebec4972025-05-02 08:46:33 -0600253 obj->bbox.x1 = obj->bbox.x0 + w;
254 obj->bbox.y1 = obj->bbox.y0 + h;
255 obj->flags |= SCENEOF_SIZE_VALID;
Simon Glass7a960052023-06-01 10:22:52 -0600256
257 return 0;
258}
259
Simon Glassc6143dc2025-05-02 08:46:35 -0600260int scene_obj_set_width(struct scene *scn, uint id, int w)
261{
262 struct scene_obj *obj;
263
264 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
265 if (!obj)
266 return log_msg_ret("find", -ENOENT);
267 obj->bbox.x1 = obj->bbox.x0 + w;
268
269 return 0;
270}
271
272int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1,
273 int y1)
274{
275 struct scene_obj *obj;
276
277 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
278 if (!obj)
279 return log_msg_ret("find", -ENOENT);
280 obj->bbox.x0 = x0;
281 obj->bbox.y0 = y0;
282 obj->bbox.x1 = x1;
283 obj->bbox.y1 = y1;
284 obj->flags |= SCENEOF_SIZE_VALID;
285
286 return 0;
287}
288
Simon Glass0a4d14b2023-01-06 08:52:37 -0600289int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
290{
Simon Glass6081b0f2023-06-01 10:22:50 -0600291 int ret;
292
293 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
294 hide ? SCENEOF_HIDE : 0);
295 if (ret)
296 return log_msg_ret("flg", ret);
297
298 return 0;
299}
300
301int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
302{
Simon Glass0a4d14b2023-01-06 08:52:37 -0600303 struct scene_obj *obj;
304
305 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
306 if (!obj)
307 return log_msg_ret("find", -ENOENT);
Simon Glass6081b0f2023-06-01 10:22:50 -0600308 obj->flags &= ~clr;
309 obj->flags |= set;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600310
311 return 0;
312}
313
314int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
315{
316 struct scene_obj *obj;
317
318 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
319 if (!obj)
320 return log_msg_ret("find", -ENOENT);
321
322 switch (obj->type) {
323 case SCENEOBJT_NONE:
324 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600325 case SCENEOBJT_TEXTLINE:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600326 break;
327 case SCENEOBJT_IMAGE: {
328 struct scene_obj_img *img = (struct scene_obj_img *)obj;
329 ulong width, height;
330 uint bpix;
331
332 video_bmp_get_info(img->data, &width, &height, &bpix);
333 if (widthp)
334 *widthp = width;
335 return height;
336 }
337 case SCENEOBJT_TEXT: {
Simon Glass9ef02aa2025-05-02 08:46:37 -0600338 struct scene_txt_generic *gen = &((struct scene_obj_txt *)obj)->gen;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600339 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600340 struct vidconsole_bbox bbox;
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600341 int len, ret, limit;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600342 const char *str;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600343
Simon Glass9ef02aa2025-05-02 08:46:37 -0600344 str = expo_get_str(exp, gen->str_id);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600345 if (!str)
346 return log_msg_ret("str", -ENOENT);
347 len = strlen(str);
348
349 /* if there is no console, make it up */
350 if (!exp->cons) {
351 if (widthp)
352 *widthp = 8 * len;
353 return 16;
354 }
355
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600356 limit = obj->flags & SCENEOF_SIZE_VALID ?
357 obj->bbox.x1 - obj->bbox.x0 : -1;
358
Simon Glass9ef02aa2025-05-02 08:46:37 -0600359 ret = vidconsole_measure(scn->expo->cons, gen->font_name,
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600360 gen->font_size, str, limit, &bbox,
361 &gen->lines);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600362 if (ret)
363 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600364 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600365 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600366
Simon Glass9e1a86d2023-06-01 10:22:51 -0600367 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600368 }
369 }
370
371 return 0;
372}
373
374/**
Simon Glass118a7272023-10-01 19:13:30 -0600375 * scene_render_background() - Render the background for an object
376 *
377 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600378 * @box_only: true to show a box around the object, but keep the normal
379 * background colour inside
Simon Glass118a7272023-10-01 19:13:30 -0600380 */
Simon Glass0023d182023-10-01 19:13:34 -0600381static void scene_render_background(struct scene_obj *obj, bool box_only)
Simon Glass118a7272023-10-01 19:13:30 -0600382{
Simon Glass96910ef2025-05-02 08:46:34 -0600383 struct vidconsole_bbox bbox[SCENEBB_count], *sel;
Simon Glass118a7272023-10-01 19:13:30 -0600384 struct expo *exp = obj->scene->expo;
385 const struct expo_theme *theme = &exp->theme;
Simon Glass118a7272023-10-01 19:13:30 -0600386 struct udevice *dev = exp->display;
387 struct video_priv *vid_priv;
388 struct udevice *cons = exp->cons;
389 struct vidconsole_colour old;
390 enum colour_idx fore, back;
391 uint inset = theme->menu_inset;
392
Simon Glass21320da2025-04-02 06:29:33 +1300393 vid_priv = dev_get_uclass_priv(dev);
Simon Glass118a7272023-10-01 19:13:30 -0600394 /* draw a background for the object */
Simon Glass21320da2025-04-02 06:29:33 +1300395 if (vid_priv->white_on_black) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600396 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600397 back = VID_WHITE;
398 } else {
399 fore = VID_LIGHT_GRAY;
400 back = VID_BLACK;
401 }
402
403 /* see if this object wants to render a background */
Simon Glass96910ef2025-05-02 08:46:34 -0600404 if (scene_obj_calc_bbox(obj, bbox))
Simon Glass118a7272023-10-01 19:13:30 -0600405 return;
406
Simon Glass96910ef2025-05-02 08:46:34 -0600407 sel = &bbox[SCENEBB_label];
408 if (!sel->valid)
409 return;
410
Simon Glass118a7272023-10-01 19:13:30 -0600411 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass96910ef2025-05-02 08:46:34 -0600412 video_fill_part(dev, sel->x0 - inset, sel->y0 - inset,
413 sel->x1 + inset, sel->y1 + inset,
Simon Glass118a7272023-10-01 19:13:30 -0600414 vid_priv->colour_fg);
415 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600416 if (box_only) {
Simon Glass96910ef2025-05-02 08:46:34 -0600417 video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1,
Simon Glass0023d182023-10-01 19:13:34 -0600418 vid_priv->colour_bg);
419 }
Simon Glass118a7272023-10-01 19:13:30 -0600420}
421
Simon Glassa841d1a2025-05-02 08:46:38 -0600422static int scene_txt_render(struct expo *exp, struct udevice *dev,
423 struct udevice *cons, struct scene_obj *obj,
424 struct scene_txt_generic *gen, int x, int y,
425 int menu_inset)
426{
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600427 const struct vidconsole_mline *mline;
Simon Glass62f39d22025-05-02 08:46:39 -0600428 struct video_priv *vid_priv;
429 struct vidconsole_colour old;
430 enum colour_idx fore, back;
Simon Glassa841d1a2025-05-02 08:46:38 -0600431 const char *str;
432 int ret;
433
434 if (!cons)
435 return -ENOTSUPP;
436
437 if (gen->font_name || gen->font_size) {
438 ret = vidconsole_select_font(cons, gen->font_name,
439 gen->font_size);
440 } else {
441 ret = vidconsole_select_font(cons, NULL, 0);
442 }
443 if (ret && ret != -ENOSYS)
444 return log_msg_ret("font", ret);
445 str = expo_get_str(exp, gen->str_id);
Simon Glass62f39d22025-05-02 08:46:39 -0600446 if (!str)
447 return 0;
Simon Glassa841d1a2025-05-02 08:46:38 -0600448
Simon Glass62f39d22025-05-02 08:46:39 -0600449 vid_priv = dev_get_uclass_priv(dev);
450 if (vid_priv->white_on_black) {
451 fore = VID_BLACK;
452 back = VID_WHITE;
453 } else {
454 fore = VID_LIGHT_GRAY;
455 back = VID_BLACK;
456 }
Simon Glassa841d1a2025-05-02 08:46:38 -0600457
Simon Glass62f39d22025-05-02 08:46:39 -0600458 if (obj->flags & SCENEOF_POINT) {
459 vidconsole_push_colour(cons, fore, back, &old);
460 video_fill_part(dev, x - menu_inset, y, obj->bbox.x1,
461 obj->bbox.y1, vid_priv->colour_bg);
Simon Glassa841d1a2025-05-02 08:46:38 -0600462 }
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600463
464 if (!gen->lines.count) {
465 vidconsole_set_cursor_pos(cons, x, y);
466 vidconsole_put_string(cons, str);
467 }
468 alist_for_each(mline, &gen->lines) {
469 vidconsole_set_cursor_pos(cons, x + mline->bbox.x0,
470 y + mline->bbox.y0);
471 vidconsole_put_stringn(cons, str + mline->start, mline->len);
472 }
Simon Glass62f39d22025-05-02 08:46:39 -0600473 if (obj->flags & SCENEOF_POINT)
474 vidconsole_pop_colour(cons, &old);
Simon Glassa841d1a2025-05-02 08:46:38 -0600475
476 return 0;
477}
478
Simon Glass118a7272023-10-01 19:13:30 -0600479/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600480 * scene_obj_render() - Render an object
481 *
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600482 * @obj: Object to render
483 * @text_mode: true to use text mode
484 * Return: 0 if OK, -ve on error
Simon Glass0a4d14b2023-01-06 08:52:37 -0600485 */
486static int scene_obj_render(struct scene_obj *obj, bool text_mode)
487{
488 struct scene *scn = obj->scene;
489 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600490 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600491 struct udevice *dev = exp->display;
492 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600493 int x, y, ret;
494
Simon Glassbc3a15f2025-05-02 08:46:31 -0600495 x = obj->bbox.x0;
496 y = obj->bbox.y0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600497
498 switch (obj->type) {
499 case SCENEOBJT_NONE:
500 break;
501 case SCENEOBJT_IMAGE: {
502 struct scene_obj_img *img = (struct scene_obj_img *)obj;
503
504 if (!cons)
505 return -ENOTSUPP;
506 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
507 true);
508 if (ret < 0)
509 return log_msg_ret("img", ret);
510 break;
511 }
512 case SCENEOBJT_TEXT: {
Simon Glassa841d1a2025-05-02 08:46:38 -0600513 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600514
Simon Glassa841d1a2025-05-02 08:46:38 -0600515 ret = scene_txt_render(exp, dev, cons, obj, &txt->gen, x, y,
516 theme->menu_inset);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600517 break;
518 }
519 case SCENEOBJT_MENU: {
520 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600521
522 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
523 if (!cons)
524 return -ENOTSUPP;
525
526 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600527 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600528 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600529 /*
530 * With a vidconsole, the text and item pointer are rendered as
531 * normal objects so we don't need to do anything here. The menu
532 * simply controls where they are positioned.
533 */
534 if (cons)
535 return -ENOTSUPP;
536
537 ret = scene_menu_display(menu);
538 if (ret < 0)
539 return log_msg_ret("img", ret);
540
541 break;
542 }
Simon Glass0023d182023-10-01 19:13:34 -0600543 case SCENEOBJT_TEXTLINE:
544 if (obj->flags & SCENEOF_OPEN)
545 scene_render_background(obj, true);
546 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600547 }
548
549 return 0;
550}
551
Simon Glass377f18e2024-10-14 16:31:55 -0600552int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
553{
554 struct scene_obj *obj;
555
556 arr->label_width = 0;
557 list_for_each_entry(obj, &scn->obj_head, sibling) {
558 uint label_id = 0;
559 int width;
560
561 switch (obj->type) {
562 case SCENEOBJT_NONE:
563 case SCENEOBJT_IMAGE:
564 case SCENEOBJT_TEXT:
565 break;
566 case SCENEOBJT_MENU: {
567 struct scene_obj_menu *menu;
568
569 menu = (struct scene_obj_menu *)obj,
570 label_id = menu->title_id;
571 break;
572 }
573 case SCENEOBJT_TEXTLINE: {
574 struct scene_obj_textline *tline;
575
576 tline = (struct scene_obj_textline *)obj,
577 label_id = tline->label_id;
578 break;
579 }
580 }
581
582 if (label_id) {
583 int ret;
584
585 ret = scene_obj_get_hw(scn, label_id, &width);
586 if (ret < 0)
587 return log_msg_ret("hei", ret);
588 arr->label_width = max(arr->label_width, width);
589 }
590 }
591
592 return 0;
593}
594
Simon Glass0a4d14b2023-01-06 08:52:37 -0600595int scene_arrange(struct scene *scn)
596{
Simon Glass377f18e2024-10-14 16:31:55 -0600597 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600598 struct scene_obj *obj;
599 int ret;
600
Simon Glass377f18e2024-10-14 16:31:55 -0600601 ret = scene_calc_arrange(scn, &arr);
602 if (ret < 0)
603 return log_msg_ret("arr", ret);
604
Simon Glass0a4d14b2023-01-06 08:52:37 -0600605 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600606 switch (obj->type) {
607 case SCENEOBJT_NONE:
608 case SCENEOBJT_IMAGE:
609 case SCENEOBJT_TEXT:
610 break;
611 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600612 struct scene_obj_menu *menu;
613
614 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600615 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600616 if (ret)
617 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600618 break;
619 }
Simon Glass0023d182023-10-01 19:13:34 -0600620 case SCENEOBJT_TEXTLINE: {
621 struct scene_obj_textline *tline;
622
623 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600624 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600625 if (ret)
626 return log_msg_ret("arr", ret);
627 break;
628 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600629 }
630 }
631
632 return 0;
633}
634
Simon Glass12f57732023-06-01 10:22:58 -0600635int scene_render_deps(struct scene *scn, uint id)
636{
637 struct scene_obj *obj;
638 int ret;
639
640 if (!id)
641 return 0;
642 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
643 if (!obj)
644 return log_msg_ret("obj", -ENOENT);
645
646 if (!(obj->flags & SCENEOF_HIDE)) {
647 ret = scene_obj_render(obj, false);
648 if (ret && ret != -ENOTSUPP)
649 return log_msg_ret("ren", ret);
650
Simon Glassb7a64532023-10-01 19:13:24 -0600651 switch (obj->type) {
652 case SCENEOBJT_NONE:
653 case SCENEOBJT_IMAGE:
654 case SCENEOBJT_TEXT:
655 break;
656 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600657 scene_menu_render_deps(scn,
658 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600659 break;
Simon Glass0023d182023-10-01 19:13:34 -0600660 case SCENEOBJT_TEXTLINE:
661 scene_textline_render_deps(scn,
662 (struct scene_obj_textline *)obj);
663 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600664 }
Simon Glass12f57732023-06-01 10:22:58 -0600665 }
666
667 return 0;
668}
669
Simon Glass0a4d14b2023-01-06 08:52:37 -0600670int scene_render(struct scene *scn)
671{
672 struct expo *exp = scn->expo;
673 struct scene_obj *obj;
674 int ret;
675
676 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600677 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600678 ret = scene_obj_render(obj, exp->text_mode);
679 if (ret && ret != -ENOTSUPP)
680 return log_msg_ret("ren", ret);
681 }
682 }
683
Simon Glass12f57732023-06-01 10:22:58 -0600684 /* render any highlighted object on top of the others */
685 if (scn->highlight_id && !exp->text_mode) {
686 ret = scene_render_deps(scn, scn->highlight_id);
687 if (ret && ret != -ENOTSUPP)
688 return log_msg_ret("dep", ret);
689 }
690
Simon Glass0a4d14b2023-01-06 08:52:37 -0600691 return 0;
692}
693
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600694/**
695 * send_key_obj() - Handle a keypress for moving between objects
696 *
697 * @scn: Scene to receive the key
698 * @key: Key to send (KEYCODE_UP)
699 * @event: Returns resulting event from this keypress
700 * Returns: 0 if OK, -ve on error
701 */
702static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
703 struct expo_action *event)
704{
705 switch (key) {
706 case BKEY_UP:
707 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
708 sibling)) {
709 obj = list_entry(obj->sibling.prev,
710 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600711 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600712 event->type = EXPOACT_POINT_OBJ;
713 event->select.id = obj->id;
714 log_debug("up to obj %d\n", event->select.id);
715 break;
716 }
717 }
718 break;
719 case BKEY_DOWN:
720 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
721 obj = list_entry(obj->sibling.next, struct scene_obj,
722 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600723 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600724 event->type = EXPOACT_POINT_OBJ;
725 event->select.id = obj->id;
726 log_debug("down to obj %d\n", event->select.id);
727 break;
728 }
729 }
730 break;
731 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600732 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600733 event->type = EXPOACT_OPEN;
734 event->select.id = obj->id;
735 log_debug("open obj %d\n", event->select.id);
736 }
737 break;
738 case BKEY_QUIT:
739 event->type = EXPOACT_QUIT;
740 log_debug("obj quit\n");
741 break;
742 }
743}
744
Simon Glass0a4d14b2023-01-06 08:52:37 -0600745int scene_send_key(struct scene *scn, int key, struct expo_action *event)
746{
747 struct scene_obj *obj;
748 int ret;
749
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600750 event->type = EXPOACT_NONE;
751
752 /*
753 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
754 * opened
755 */
756 if (scn->expo->popup) {
757 obj = NULL;
758 if (scn->highlight_id) {
759 obj = scene_obj_find(scn, scn->highlight_id,
760 SCENEOBJT_NONE);
761 }
762 if (!obj)
763 return 0;
764
765 if (!(obj->flags & SCENEOF_OPEN)) {
766 send_key_obj(scn, obj, key, event);
767 return 0;
768 }
769
Simon Glassb7a64532023-10-01 19:13:24 -0600770 switch (obj->type) {
771 case SCENEOBJT_NONE:
772 case SCENEOBJT_IMAGE:
773 case SCENEOBJT_TEXT:
774 break;
775 case SCENEOBJT_MENU: {
776 struct scene_obj_menu *menu;
777
778 menu = (struct scene_obj_menu *)obj,
779 ret = scene_menu_send_key(scn, menu, key, event);
780 if (ret)
781 return log_msg_ret("key", ret);
782 break;
783 }
Simon Glass0023d182023-10-01 19:13:34 -0600784 case SCENEOBJT_TEXTLINE: {
785 struct scene_obj_textline *tline;
786
787 tline = (struct scene_obj_textline *)obj,
788 ret = scene_textline_send_key(scn, tline, key, event);
789 if (ret)
790 return log_msg_ret("key", ret);
791 break;
792 }
Simon Glassb7a64532023-10-01 19:13:24 -0600793 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600794 return 0;
795 }
796
Simon Glass0a4d14b2023-01-06 08:52:37 -0600797 list_for_each_entry(obj, &scn->obj_head, sibling) {
798 if (obj->type == SCENEOBJT_MENU) {
799 struct scene_obj_menu *menu;
800
801 menu = (struct scene_obj_menu *)obj,
802 ret = scene_menu_send_key(scn, menu, key, event);
803 if (ret)
804 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600805 break;
806 }
807 }
808
809 return 0;
810}
Simon Glass7a960052023-06-01 10:22:52 -0600811
Simon Glass96910ef2025-05-02 08:46:34 -0600812int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
Simon Glassf0994692023-10-01 19:13:29 -0600813{
814 switch (obj->type) {
815 case SCENEOBJT_NONE:
816 case SCENEOBJT_IMAGE:
817 case SCENEOBJT_TEXT:
818 return -ENOSYS;
819 case SCENEOBJT_MENU: {
820 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
821
Simon Glass96910ef2025-05-02 08:46:34 -0600822 scene_menu_calc_bbox(menu, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600823 break;
824 }
Simon Glass0023d182023-10-01 19:13:34 -0600825 case SCENEOBJT_TEXTLINE: {
826 struct scene_obj_textline *tline;
827
828 tline = (struct scene_obj_textline *)obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600829 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
830 &bbox[SCENEBB_label]);
Simon Glass0023d182023-10-01 19:13:34 -0600831 break;
832 }
Simon Glassf0994692023-10-01 19:13:29 -0600833 }
834
835 return 0;
836}
837
Simon Glass7a960052023-06-01 10:22:52 -0600838int scene_calc_dims(struct scene *scn, bool do_menus)
839{
840 struct scene_obj *obj;
841 int ret;
842
843 list_for_each_entry(obj, &scn->obj_head, sibling) {
844 switch (obj->type) {
845 case SCENEOBJT_NONE:
846 case SCENEOBJT_TEXT:
847 case SCENEOBJT_IMAGE: {
848 int width;
849
850 if (!do_menus) {
851 ret = scene_obj_get_hw(scn, obj->id, &width);
852 if (ret < 0)
853 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -0600854 obj->dims.x = width;
855 obj->dims.y = ret;
856 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
857 obj->bbox.x1 = obj->bbox.x0 + width;
858 obj->bbox.y1 = obj->bbox.y0 + ret;
859 obj->flags |= SCENEOF_SIZE_VALID;
860 }
Simon Glass7a960052023-06-01 10:22:52 -0600861 }
862 break;
863 }
864 case SCENEOBJT_MENU: {
865 struct scene_obj_menu *menu;
866
867 if (do_menus) {
868 menu = (struct scene_obj_menu *)obj;
869
870 ret = scene_menu_calc_dims(menu);
871 if (ret)
872 return log_msg_ret("men", ret);
873 }
874 break;
875 }
Simon Glass0023d182023-10-01 19:13:34 -0600876 case SCENEOBJT_TEXTLINE: {
877 struct scene_obj_textline *tline;
878
879 tline = (struct scene_obj_textline *)obj;
880 ret = scene_textline_calc_dims(tline);
881 if (ret)
882 return log_msg_ret("men", ret);
883
884 break;
885 }
Simon Glass7a960052023-06-01 10:22:52 -0600886 }
887 }
888
889 return 0;
890}
Simon Glassc999e172023-06-01 10:22:53 -0600891
892int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
893{
894 struct scene_obj *obj;
895 int ret;
896
897 /* Avoid error-checking optional items */
898 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
899
900 list_for_each_entry(obj, &scn->obj_head, sibling) {
901 switch (obj->type) {
902 case SCENEOBJT_NONE:
903 case SCENEOBJT_IMAGE:
904 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600905 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600906 break;
907 case SCENEOBJT_TEXT:
908 scene_txt_set_font(scn, obj->id, NULL,
909 theme->font_size);
910 break;
911 }
912 }
913
914 ret = scene_arrange(scn);
915 if (ret)
916 return log_msg_ret("arr", ret);
917
918 return 0;
919}
Simon Glass01922ec2023-06-01 10:22:57 -0600920
921void scene_set_highlight_id(struct scene *scn, uint id)
922{
923 scn->highlight_id = id;
924}
925
926void scene_highlight_first(struct scene *scn)
927{
928 struct scene_obj *obj;
929
930 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600931 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600932 scene_set_highlight_id(scn, obj->id);
933 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600934 }
935 }
936}
937
Simon Glassf6a943a2023-10-01 19:13:33 -0600938static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
939{
940 int ret;
941
942 switch (obj->type) {
943 case SCENEOBJT_NONE:
944 case SCENEOBJT_IMAGE:
945 case SCENEOBJT_MENU:
946 case SCENEOBJT_TEXT:
947 break;
948 case SCENEOBJT_TEXTLINE:
949 ret = scene_textline_open(scn,
950 (struct scene_obj_textline *)obj);
951 if (ret)
952 return log_msg_ret("op", ret);
953 break;
954 }
955
956 return 0;
957}
958
Simon Glass01922ec2023-06-01 10:22:57 -0600959int scene_set_open(struct scene *scn, uint id, bool open)
960{
Simon Glassf6a943a2023-10-01 19:13:33 -0600961 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600962 int ret;
963
Simon Glassf6a943a2023-10-01 19:13:33 -0600964 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
965 if (!obj)
966 return log_msg_ret("find", -ENOENT);
967
968 if (open) {
969 ret = scene_obj_open(scn, obj);
970 if (ret)
971 return log_msg_ret("op", ret);
972 }
973
Simon Glass01922ec2023-06-01 10:22:57 -0600974 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
975 open ? SCENEOF_OPEN : 0);
976 if (ret)
977 return log_msg_ret("flg", ret);
978
979 return 0;
980}
Simon Glasse90acd82023-08-14 16:40:23 -0600981
982int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
983 void *priv)
984{
985 struct scene_obj *obj;
986
987 list_for_each_entry(obj, &scn->obj_head, sibling) {
988 int ret;
989
990 ret = iter(obj, priv);
991 if (ret)
992 return log_msg_ret("itr", ret);
993 }
994
995 return 0;
996}
Simon Glassf0994692023-10-01 19:13:29 -0600997
Simon Glass96910ef2025-05-02 08:46:34 -0600998int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
999 struct vidconsole_bbox *dst)
1000{
1001 if (dst->valid) {
1002 dst->x0 = min(dst->x0, src->x0 - inset);
1003 dst->y0 = min(dst->y0, src->y0);
1004 dst->x1 = max(dst->x1, src->x1 + inset);
1005 dst->y1 = max(dst->y1, src->y1);
1006 } else {
1007 dst->x0 = src->x0 - inset;
1008 dst->y0 = src->y0;
1009 dst->x1 = src->x1 + inset;
1010 dst->y1 = src->y1;
1011 dst->valid = true;
1012 }
1013
1014 return 0;
1015}
1016
Simon Glassf0994692023-10-01 19:13:29 -06001017int scene_bbox_union(struct scene *scn, uint id, int inset,
1018 struct vidconsole_bbox *bbox)
1019{
1020 struct scene_obj *obj;
Simon Glass96910ef2025-05-02 08:46:34 -06001021 struct vidconsole_bbox local;
Simon Glassf0994692023-10-01 19:13:29 -06001022
1023 if (!id)
1024 return 0;
1025 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1026 if (!obj)
1027 return log_msg_ret("obj", -ENOENT);
Simon Glass96910ef2025-05-02 08:46:34 -06001028 local.x0 = obj->bbox.x0;
1029 local.y0 = obj->bbox.y0;
1030 local.x1 = obj->bbox.x1;
1031 local.y1 = obj->bbox.y1;
1032 local.valid = true;
1033 scene_bbox_join(&local, inset, bbox);
Simon Glassf0994692023-10-01 19:13:29 -06001034
1035 return 0;
1036}