blob: 237f2bccc0010784e711422af6b037681c14d043 [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
Simon Glass138a3972025-05-02 08:46:44 -0600214int scene_box(struct scene *scn, const char *name, uint id, uint width,
215 struct scene_obj_box **boxp)
216{
217 struct scene_obj_box *box;
218 int ret;
219
220 ret = scene_obj_add(scn, name, id, SCENEOBJT_BOX,
221 sizeof(struct scene_obj_box),
222 (struct scene_obj **)&box);
223 if (ret < 0)
224 return log_msg_ret("obj", ret);
225
226 box->width = width;
227
228 if (boxp)
229 *boxp = box;
230
231 return box->obj.id;
232}
233
Simon Glass0a4d14b2023-01-06 08:52:37 -0600234int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
235 uint font_size)
236{
237 struct scene_obj_txt *txt;
238
239 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
240 if (!txt)
241 return log_msg_ret("find", -ENOENT);
Simon Glass9ef02aa2025-05-02 08:46:37 -0600242 txt->gen.font_name = font_name;
243 txt->gen.font_size = font_size;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600244
245 return 0;
246}
247
248int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
249{
250 struct scene_obj *obj;
Simon Glassebec4972025-05-02 08:46:33 -0600251 int w, h;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600252
253 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
254 if (!obj)
255 return log_msg_ret("find", -ENOENT);
Simon Glassebec4972025-05-02 08:46:33 -0600256 w = obj->bbox.x1 - obj->bbox.x0;
257 h = obj->bbox.y1 - obj->bbox.y0;
Simon Glassbc3a15f2025-05-02 08:46:31 -0600258 obj->bbox.x0 = x;
259 obj->bbox.y0 = y;
Simon Glassebec4972025-05-02 08:46:33 -0600260 obj->bbox.x1 = obj->bbox.x0 + w;
261 obj->bbox.y1 = obj->bbox.y0 + h;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600262
263 return 0;
264}
265
Simon Glass7a960052023-06-01 10:22:52 -0600266int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
267{
268 struct scene_obj *obj;
269
270 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
271 if (!obj)
272 return log_msg_ret("find", -ENOENT);
Simon Glassebec4972025-05-02 08:46:33 -0600273 obj->bbox.x1 = obj->bbox.x0 + w;
274 obj->bbox.y1 = obj->bbox.y0 + h;
275 obj->flags |= SCENEOF_SIZE_VALID;
Simon Glass7a960052023-06-01 10:22:52 -0600276
277 return 0;
278}
279
Simon Glassc6143dc2025-05-02 08:46:35 -0600280int scene_obj_set_width(struct scene *scn, uint id, int w)
281{
282 struct scene_obj *obj;
283
284 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
285 if (!obj)
286 return log_msg_ret("find", -ENOENT);
287 obj->bbox.x1 = obj->bbox.x0 + w;
288
289 return 0;
290}
291
292int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1,
293 int y1)
294{
295 struct scene_obj *obj;
296
297 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
298 if (!obj)
299 return log_msg_ret("find", -ENOENT);
300 obj->bbox.x0 = x0;
301 obj->bbox.y0 = y0;
302 obj->bbox.x1 = x1;
303 obj->bbox.y1 = y1;
304 obj->flags |= SCENEOF_SIZE_VALID;
305
306 return 0;
307}
308
Simon Glass5beb0572025-05-02 08:46:45 -0600309int scene_obj_set_halign(struct scene *scn, uint id, enum scene_obj_align aln)
310{
311 struct scene_obj *obj;
312
313 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
314 if (!obj)
315 return log_msg_ret("osh", -ENOENT);
316 obj->horiz = aln;
317
318 return 0;
319}
320
321int scene_obj_set_valign(struct scene *scn, uint id, enum scene_obj_align aln)
322{
323 struct scene_obj *obj;
324
325 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
326 if (!obj)
327 return log_msg_ret("osv", -ENOENT);
328 obj->vert = aln;
329
330 return 0;
331}
332
Simon Glass0a4d14b2023-01-06 08:52:37 -0600333int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
334{
Simon Glass6081b0f2023-06-01 10:22:50 -0600335 int ret;
336
337 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
338 hide ? SCENEOF_HIDE : 0);
339 if (ret)
340 return log_msg_ret("flg", ret);
341
342 return 0;
343}
344
345int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
346{
Simon Glass0a4d14b2023-01-06 08:52:37 -0600347 struct scene_obj *obj;
348
349 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
350 if (!obj)
351 return log_msg_ret("find", -ENOENT);
Simon Glass6081b0f2023-06-01 10:22:50 -0600352 obj->flags &= ~clr;
353 obj->flags |= set;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600354
355 return 0;
356}
357
Simon Glass5beb0572025-05-02 08:46:45 -0600358static void handle_alignment(enum scene_obj_align horiz,
359 enum scene_obj_align vert,
360 struct scene_obj_bbox *bbox,
361 struct scene_obj_dims *dims,
362 int xsize, int ysize,
363 struct scene_obj_offset *offset)
364{
365 int width, height;
366
367 width = bbox->x1 - bbox->x0;
368 height = bbox->y1 - bbox->y0;
369
370 switch (horiz) {
371 case SCENEOA_CENTRE:
372 offset->xofs = (width - dims->x) / 2;
373 break;
374 case SCENEOA_RIGHT:
375 offset->xofs = width - dims->x;
376 break;
377 case SCENEOA_LEFT:
378 offset->xofs = 0;
379 break;
380 }
381
382 switch (vert) {
383 case SCENEOA_CENTRE:
384 offset->yofs = (height - dims->y) / 2;
385 break;
386 case SCENEOA_BOTTOM:
387 offset->yofs = height - dims->y;
388 break;
389 case SCENEOA_TOP:
390 default:
391 offset->yofs = 0;
392 break;
393 }
394}
395
Simon Glass0a4d14b2023-01-06 08:52:37 -0600396int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
397{
398 struct scene_obj *obj;
399
400 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
401 if (!obj)
402 return log_msg_ret("find", -ENOENT);
403
404 switch (obj->type) {
405 case SCENEOBJT_NONE:
406 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600407 case SCENEOBJT_TEXTLINE:
Simon Glass138a3972025-05-02 08:46:44 -0600408 case SCENEOBJT_BOX:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600409 break;
410 case SCENEOBJT_IMAGE: {
411 struct scene_obj_img *img = (struct scene_obj_img *)obj;
412 ulong width, height;
413 uint bpix;
414
415 video_bmp_get_info(img->data, &width, &height, &bpix);
416 if (widthp)
417 *widthp = width;
418 return height;
419 }
Simon Glass5dc887d2025-05-02 08:46:46 -0600420 case SCENEOBJT_TEXT:
421 case SCENEOBJT_TEXTEDIT: {
422 struct scene_txt_generic *gen;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600423 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600424 struct vidconsole_bbox bbox;
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600425 int len, ret, limit;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600426 const char *str;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600427
Simon Glass5dc887d2025-05-02 08:46:46 -0600428 if (obj->type == SCENEOBJT_TEXT)
429 gen = &((struct scene_obj_txt *)obj)->gen;
430 else
431 gen = &((struct scene_obj_txtedit *)obj)->gen;
432
Simon Glass9ef02aa2025-05-02 08:46:37 -0600433 str = expo_get_str(exp, gen->str_id);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600434 if (!str)
435 return log_msg_ret("str", -ENOENT);
436 len = strlen(str);
437
438 /* if there is no console, make it up */
439 if (!exp->cons) {
440 if (widthp)
441 *widthp = 8 * len;
442 return 16;
443 }
444
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600445 limit = obj->flags & SCENEOF_SIZE_VALID ?
446 obj->bbox.x1 - obj->bbox.x0 : -1;
447
Simon Glass9ef02aa2025-05-02 08:46:37 -0600448 ret = vidconsole_measure(scn->expo->cons, gen->font_name,
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600449 gen->font_size, str, limit, &bbox,
450 &gen->lines);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600451 if (ret)
452 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600453 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600454 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600455
Simon Glass9e1a86d2023-06-01 10:22:51 -0600456 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600457 }
458 }
459
460 return 0;
461}
462
463/**
Simon Glass118a7272023-10-01 19:13:30 -0600464 * scene_render_background() - Render the background for an object
465 *
466 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600467 * @box_only: true to show a box around the object, but keep the normal
468 * background colour inside
Simon Glass2d857262025-05-02 08:46:50 -0600469 * @cur_item: true to render the background only for the current menu item
Simon Glass118a7272023-10-01 19:13:30 -0600470 */
Simon Glass2d857262025-05-02 08:46:50 -0600471static void scene_render_background(struct scene_obj *obj, bool box_only,
472 bool cur_item)
Simon Glass118a7272023-10-01 19:13:30 -0600473{
Simon Glass96910ef2025-05-02 08:46:34 -0600474 struct vidconsole_bbox bbox[SCENEBB_count], *sel;
Simon Glass118a7272023-10-01 19:13:30 -0600475 struct expo *exp = obj->scene->expo;
476 const struct expo_theme *theme = &exp->theme;
Simon Glass118a7272023-10-01 19:13:30 -0600477 struct udevice *dev = exp->display;
478 struct video_priv *vid_priv;
479 struct udevice *cons = exp->cons;
480 struct vidconsole_colour old;
481 enum colour_idx fore, back;
482 uint inset = theme->menu_inset;
483
Simon Glass21320da2025-04-02 06:29:33 +1300484 vid_priv = dev_get_uclass_priv(dev);
Simon Glass118a7272023-10-01 19:13:30 -0600485 /* draw a background for the object */
Simon Glass21320da2025-04-02 06:29:33 +1300486 if (vid_priv->white_on_black) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600487 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600488 back = VID_WHITE;
489 } else {
490 fore = VID_LIGHT_GRAY;
491 back = VID_BLACK;
492 }
493
494 /* see if this object wants to render a background */
Simon Glass96910ef2025-05-02 08:46:34 -0600495 if (scene_obj_calc_bbox(obj, bbox))
Simon Glass118a7272023-10-01 19:13:30 -0600496 return;
497
Simon Glass2d857262025-05-02 08:46:50 -0600498 sel = cur_item ? &bbox[SCENEBB_curitem] : &bbox[SCENEBB_label];
Simon Glass96910ef2025-05-02 08:46:34 -0600499 if (!sel->valid)
500 return;
501
Simon Glass118a7272023-10-01 19:13:30 -0600502 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass96910ef2025-05-02 08:46:34 -0600503 video_fill_part(dev, sel->x0 - inset, sel->y0 - inset,
504 sel->x1 + inset, sel->y1 + inset,
Simon Glass118a7272023-10-01 19:13:30 -0600505 vid_priv->colour_fg);
506 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600507 if (box_only) {
Simon Glass96910ef2025-05-02 08:46:34 -0600508 video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1,
Simon Glass0023d182023-10-01 19:13:34 -0600509 vid_priv->colour_bg);
510 }
Simon Glass118a7272023-10-01 19:13:30 -0600511}
512
Simon Glassa841d1a2025-05-02 08:46:38 -0600513static int scene_txt_render(struct expo *exp, struct udevice *dev,
514 struct udevice *cons, struct scene_obj *obj,
515 struct scene_txt_generic *gen, int x, int y,
516 int menu_inset)
517{
Simon Glass5beb0572025-05-02 08:46:45 -0600518 const struct vidconsole_mline *mline, *last;
Simon Glass62f39d22025-05-02 08:46:39 -0600519 struct video_priv *vid_priv;
520 struct vidconsole_colour old;
521 enum colour_idx fore, back;
Simon Glass5beb0572025-05-02 08:46:45 -0600522 struct scene_obj_dims dims;
523 struct scene_obj_bbox bbox;
Simon Glassa841d1a2025-05-02 08:46:38 -0600524 const char *str;
525 int ret;
526
527 if (!cons)
528 return -ENOTSUPP;
529
530 if (gen->font_name || gen->font_size) {
531 ret = vidconsole_select_font(cons, gen->font_name,
532 gen->font_size);
533 } else {
534 ret = vidconsole_select_font(cons, NULL, 0);
535 }
536 if (ret && ret != -ENOSYS)
537 return log_msg_ret("font", ret);
538 str = expo_get_str(exp, gen->str_id);
Simon Glass62f39d22025-05-02 08:46:39 -0600539 if (!str)
540 return 0;
Simon Glassa841d1a2025-05-02 08:46:38 -0600541
Simon Glass62f39d22025-05-02 08:46:39 -0600542 vid_priv = dev_get_uclass_priv(dev);
543 if (vid_priv->white_on_black) {
544 fore = VID_BLACK;
545 back = VID_WHITE;
546 } else {
547 fore = VID_LIGHT_GRAY;
548 back = VID_BLACK;
549 }
Simon Glassa841d1a2025-05-02 08:46:38 -0600550
Simon Glass62f39d22025-05-02 08:46:39 -0600551 if (obj->flags & SCENEOF_POINT) {
Simon Glass2d857262025-05-02 08:46:50 -0600552 int inset;
553
554 inset = exp->popup ? menu_inset : 0;
Simon Glass62f39d22025-05-02 08:46:39 -0600555 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass2d857262025-05-02 08:46:50 -0600556 video_fill_part(dev, x - inset, y,
557 obj->bbox.x1, obj->bbox.y1,
558 vid_priv->colour_bg);
Simon Glassa841d1a2025-05-02 08:46:38 -0600559 }
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600560
Simon Glass5beb0572025-05-02 08:46:45 -0600561 mline = alist_get(&gen->lines, 0, typeof(*mline));
562 last = alist_get(&gen->lines, gen->lines.count - 1, typeof(*mline));
563 if (mline)
564 dims.y = last->bbox.y1 - mline->bbox.y0;
565 bbox.y0 = obj->bbox.y0;
566 bbox.y1 = obj->bbox.y1;
567
568 if (!mline) {
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600569 vidconsole_set_cursor_pos(cons, x, y);
570 vidconsole_put_string(cons, str);
571 }
Simon Glass5beb0572025-05-02 08:46:45 -0600572
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600573 alist_for_each(mline, &gen->lines) {
Simon Glass5beb0572025-05-02 08:46:45 -0600574 struct scene_obj_offset offset;
575
576 bbox.x0 = obj->bbox.x0;
577 bbox.x1 = obj->bbox.x1;
578 dims.x = mline->bbox.x1 - mline->bbox.x0;
579 handle_alignment(obj->horiz, obj->vert, &bbox, &dims,
580 obj->bbox.x1 - obj->bbox.x0,
581 obj->bbox.y1 - obj->bbox.y0, &offset);
582
583 x = obj->bbox.x0 + offset.xofs;
584 y = obj->bbox.y0 + offset.yofs + mline->bbox.y0;
585 if (y > bbox.y1)
586 break; /* clip this line and any following */
587 vidconsole_set_cursor_pos(cons, x, y);
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600588 vidconsole_put_stringn(cons, str + mline->start, mline->len);
589 }
Simon Glass62f39d22025-05-02 08:46:39 -0600590 if (obj->flags & SCENEOF_POINT)
591 vidconsole_pop_colour(cons, &old);
Simon Glassa841d1a2025-05-02 08:46:38 -0600592
593 return 0;
594}
595
Simon Glass118a7272023-10-01 19:13:30 -0600596/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600597 * scene_obj_render() - Render an object
598 *
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600599 * @obj: Object to render
600 * @text_mode: true to use text mode
601 * Return: 0 if OK, -ve on error
Simon Glass0a4d14b2023-01-06 08:52:37 -0600602 */
603static int scene_obj_render(struct scene_obj *obj, bool text_mode)
604{
605 struct scene *scn = obj->scene;
606 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600607 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600608 struct udevice *dev = exp->display;
609 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass138a3972025-05-02 08:46:44 -0600610 struct video_priv *vid_priv;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600611 int x, y, ret;
612
Simon Glassbc3a15f2025-05-02 08:46:31 -0600613 y = obj->bbox.y0;
Simon Glass5beb0572025-05-02 08:46:45 -0600614 x = obj->bbox.x0 + obj->ofs.xofs;
Simon Glass138a3972025-05-02 08:46:44 -0600615 vid_priv = dev_get_uclass_priv(dev);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600616
617 switch (obj->type) {
618 case SCENEOBJT_NONE:
619 break;
620 case SCENEOBJT_IMAGE: {
621 struct scene_obj_img *img = (struct scene_obj_img *)obj;
622
623 if (!cons)
624 return -ENOTSUPP;
625 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
626 true);
627 if (ret < 0)
628 return log_msg_ret("img", ret);
629 break;
630 }
631 case SCENEOBJT_TEXT: {
Simon Glassa841d1a2025-05-02 08:46:38 -0600632 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600633
Simon Glassa841d1a2025-05-02 08:46:38 -0600634 ret = scene_txt_render(exp, dev, cons, obj, &txt->gen, x, y,
635 theme->menu_inset);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600636 break;
637 }
638 case SCENEOBJT_MENU: {
639 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600640
Simon Glass2d857262025-05-02 08:46:50 -0600641 if (exp->popup) {
642 if (obj->flags & SCENEOF_OPEN) {
643 if (!cons)
644 return -ENOTSUPP;
Simon Glass01922ec2023-06-01 10:22:57 -0600645
Simon Glass2d857262025-05-02 08:46:50 -0600646 /* draw a background behind the menu items */
647 scene_render_background(obj, false, false);
648 }
649 } else if (exp->show_highlight) {
650 /* do nothing */
Simon Glass01922ec2023-06-01 10:22:57 -0600651 }
Simon Glass2d857262025-05-02 08:46:50 -0600652
Simon Glass0a4d14b2023-01-06 08:52:37 -0600653 /*
654 * With a vidconsole, the text and item pointer are rendered as
655 * normal objects so we don't need to do anything here. The menu
656 * simply controls where they are positioned.
657 */
658 if (cons)
659 return -ENOTSUPP;
660
661 ret = scene_menu_display(menu);
662 if (ret < 0)
663 return log_msg_ret("img", ret);
664
665 break;
666 }
Simon Glass0023d182023-10-01 19:13:34 -0600667 case SCENEOBJT_TEXTLINE:
668 if (obj->flags & SCENEOF_OPEN)
Simon Glass2d857262025-05-02 08:46:50 -0600669 scene_render_background(obj, true, false);
Simon Glass0023d182023-10-01 19:13:34 -0600670 break;
Simon Glass138a3972025-05-02 08:46:44 -0600671 case SCENEOBJT_BOX: {
672 struct scene_obj_box *box = (struct scene_obj_box *)obj;
673
674 video_draw_box(dev, obj->bbox.x0, obj->bbox.y0, obj->bbox.x1,
675 obj->bbox.y1, box->width, vid_priv->colour_fg);
676 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600677 }
Simon Glass5dc887d2025-05-02 08:46:46 -0600678 case SCENEOBJT_TEXTEDIT: {
679 struct scene_obj_txtedit *ted = (struct scene_obj_txtedit *)obj;
680
681 ret = scene_txt_render(exp, dev, cons, obj, &ted->gen, x, y,
682 theme->menu_inset);
683 break;
684 }
Simon Glass138a3972025-05-02 08:46:44 -0600685 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600686
687 return 0;
688}
689
Simon Glass377f18e2024-10-14 16:31:55 -0600690int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
691{
692 struct scene_obj *obj;
693
694 arr->label_width = 0;
695 list_for_each_entry(obj, &scn->obj_head, sibling) {
696 uint label_id = 0;
697 int width;
698
699 switch (obj->type) {
700 case SCENEOBJT_NONE:
701 case SCENEOBJT_IMAGE:
702 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600703 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -0600704 case SCENEOBJT_TEXTEDIT:
Simon Glass377f18e2024-10-14 16:31:55 -0600705 break;
706 case SCENEOBJT_MENU: {
707 struct scene_obj_menu *menu;
708
709 menu = (struct scene_obj_menu *)obj,
710 label_id = menu->title_id;
711 break;
712 }
713 case SCENEOBJT_TEXTLINE: {
714 struct scene_obj_textline *tline;
715
716 tline = (struct scene_obj_textline *)obj,
717 label_id = tline->label_id;
718 break;
719 }
720 }
721
722 if (label_id) {
723 int ret;
724
725 ret = scene_obj_get_hw(scn, label_id, &width);
726 if (ret < 0)
727 return log_msg_ret("hei", ret);
728 arr->label_width = max(arr->label_width, width);
729 }
730 }
731
732 return 0;
733}
734
Simon Glass0a4d14b2023-01-06 08:52:37 -0600735int scene_arrange(struct scene *scn)
736{
Simon Glass377f18e2024-10-14 16:31:55 -0600737 struct expo_arrange_info arr;
Simon Glass5beb0572025-05-02 08:46:45 -0600738 int xsize = 0, ysize = 0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600739 struct scene_obj *obj;
Simon Glass5beb0572025-05-02 08:46:45 -0600740 struct udevice *dev;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600741 int ret;
742
Simon Glass5beb0572025-05-02 08:46:45 -0600743 dev = scn->expo->display;
744 if (dev) {
745 struct video_priv *priv = dev_get_uclass_priv(dev);
746
747 xsize = priv->xsize;
748 ysize = priv->ysize;
749 }
750
Simon Glass377f18e2024-10-14 16:31:55 -0600751 ret = scene_calc_arrange(scn, &arr);
752 if (ret < 0)
753 return log_msg_ret("arr", ret);
754
Simon Glass0a4d14b2023-01-06 08:52:37 -0600755 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass5beb0572025-05-02 08:46:45 -0600756 handle_alignment(obj->horiz, obj->vert, &obj->bbox, &obj->dims,
757 xsize, ysize, &obj->ofs);
758
Simon Glassb7a64532023-10-01 19:13:24 -0600759 switch (obj->type) {
760 case SCENEOBJT_NONE:
761 case SCENEOBJT_IMAGE:
762 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600763 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -0600764 case SCENEOBJT_TEXTEDIT:
Simon Glassb7a64532023-10-01 19:13:24 -0600765 break;
766 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600767 struct scene_obj_menu *menu;
768
769 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600770 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600771 if (ret)
772 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600773 break;
774 }
Simon Glass0023d182023-10-01 19:13:34 -0600775 case SCENEOBJT_TEXTLINE: {
776 struct scene_obj_textline *tline;
777
778 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600779 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600780 if (ret)
781 return log_msg_ret("arr", ret);
782 break;
783 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600784 }
785 }
786
787 return 0;
788}
789
Simon Glass12f57732023-06-01 10:22:58 -0600790int scene_render_deps(struct scene *scn, uint id)
791{
792 struct scene_obj *obj;
793 int ret;
794
795 if (!id)
796 return 0;
797 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
798 if (!obj)
799 return log_msg_ret("obj", -ENOENT);
800
801 if (!(obj->flags & SCENEOF_HIDE)) {
802 ret = scene_obj_render(obj, false);
803 if (ret && ret != -ENOTSUPP)
804 return log_msg_ret("ren", ret);
805
Simon Glassb7a64532023-10-01 19:13:24 -0600806 switch (obj->type) {
807 case SCENEOBJT_NONE:
808 case SCENEOBJT_IMAGE:
809 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600810 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -0600811 case SCENEOBJT_TEXTEDIT:
Simon Glassb7a64532023-10-01 19:13:24 -0600812 break;
813 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600814 scene_menu_render_deps(scn,
815 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600816 break;
Simon Glass0023d182023-10-01 19:13:34 -0600817 case SCENEOBJT_TEXTLINE:
818 scene_textline_render_deps(scn,
819 (struct scene_obj_textline *)obj);
820 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600821 }
Simon Glass12f57732023-06-01 10:22:58 -0600822 }
823
824 return 0;
825}
826
Simon Glass0a4d14b2023-01-06 08:52:37 -0600827int scene_render(struct scene *scn)
828{
829 struct expo *exp = scn->expo;
830 struct scene_obj *obj;
831 int ret;
832
833 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600834 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600835 ret = scene_obj_render(obj, exp->text_mode);
836 if (ret && ret != -ENOTSUPP)
837 return log_msg_ret("ren", ret);
838 }
839 }
840
Simon Glass12f57732023-06-01 10:22:58 -0600841 /* render any highlighted object on top of the others */
842 if (scn->highlight_id && !exp->text_mode) {
843 ret = scene_render_deps(scn, scn->highlight_id);
844 if (ret && ret != -ENOTSUPP)
845 return log_msg_ret("dep", ret);
846 }
847
Simon Glass0a4d14b2023-01-06 08:52:37 -0600848 return 0;
849}
850
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600851/**
852 * send_key_obj() - Handle a keypress for moving between objects
853 *
854 * @scn: Scene to receive the key
855 * @key: Key to send (KEYCODE_UP)
856 * @event: Returns resulting event from this keypress
857 * Returns: 0 if OK, -ve on error
858 */
859static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
860 struct expo_action *event)
861{
862 switch (key) {
863 case BKEY_UP:
864 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
865 sibling)) {
866 obj = list_entry(obj->sibling.prev,
867 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600868 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600869 event->type = EXPOACT_POINT_OBJ;
870 event->select.id = obj->id;
871 log_debug("up to obj %d\n", event->select.id);
872 break;
873 }
874 }
875 break;
876 case BKEY_DOWN:
877 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
878 obj = list_entry(obj->sibling.next, struct scene_obj,
879 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600880 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600881 event->type = EXPOACT_POINT_OBJ;
882 event->select.id = obj->id;
883 log_debug("down to obj %d\n", event->select.id);
884 break;
885 }
886 }
887 break;
888 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600889 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600890 event->type = EXPOACT_OPEN;
891 event->select.id = obj->id;
892 log_debug("open obj %d\n", event->select.id);
893 }
894 break;
895 case BKEY_QUIT:
896 event->type = EXPOACT_QUIT;
897 log_debug("obj quit\n");
898 break;
899 }
900}
901
Simon Glass0a4d14b2023-01-06 08:52:37 -0600902int scene_send_key(struct scene *scn, int key, struct expo_action *event)
903{
904 struct scene_obj *obj;
905 int ret;
906
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600907 event->type = EXPOACT_NONE;
908
909 /*
910 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
911 * opened
912 */
913 if (scn->expo->popup) {
914 obj = NULL;
915 if (scn->highlight_id) {
916 obj = scene_obj_find(scn, scn->highlight_id,
917 SCENEOBJT_NONE);
918 }
919 if (!obj)
920 return 0;
921
922 if (!(obj->flags & SCENEOF_OPEN)) {
923 send_key_obj(scn, obj, key, event);
924 return 0;
925 }
926
Simon Glassb7a64532023-10-01 19:13:24 -0600927 switch (obj->type) {
928 case SCENEOBJT_NONE:
929 case SCENEOBJT_IMAGE:
930 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600931 case SCENEOBJT_BOX:
Simon Glassb7a64532023-10-01 19:13:24 -0600932 break;
933 case SCENEOBJT_MENU: {
934 struct scene_obj_menu *menu;
935
936 menu = (struct scene_obj_menu *)obj,
937 ret = scene_menu_send_key(scn, menu, key, event);
938 if (ret)
939 return log_msg_ret("key", ret);
940 break;
941 }
Simon Glass0023d182023-10-01 19:13:34 -0600942 case SCENEOBJT_TEXTLINE: {
943 struct scene_obj_textline *tline;
944
945 tline = (struct scene_obj_textline *)obj,
946 ret = scene_textline_send_key(scn, tline, key, event);
947 if (ret)
948 return log_msg_ret("key", ret);
949 break;
950 }
Simon Glass5dc887d2025-05-02 08:46:46 -0600951 case SCENEOBJT_TEXTEDIT:
952 /* TODO(sjg@chromium.org): Implement this */
953 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600954 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600955 return 0;
956 }
957
Simon Glass0a4d14b2023-01-06 08:52:37 -0600958 list_for_each_entry(obj, &scn->obj_head, sibling) {
959 if (obj->type == SCENEOBJT_MENU) {
960 struct scene_obj_menu *menu;
961
962 menu = (struct scene_obj_menu *)obj,
963 ret = scene_menu_send_key(scn, menu, key, event);
964 if (ret)
965 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600966 break;
967 }
968 }
969
970 return 0;
971}
Simon Glass7a960052023-06-01 10:22:52 -0600972
Simon Glass96910ef2025-05-02 08:46:34 -0600973int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
Simon Glassf0994692023-10-01 19:13:29 -0600974{
975 switch (obj->type) {
976 case SCENEOBJT_NONE:
977 case SCENEOBJT_IMAGE:
978 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600979 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -0600980 case SCENEOBJT_TEXTEDIT:
Simon Glassf0994692023-10-01 19:13:29 -0600981 return -ENOSYS;
982 case SCENEOBJT_MENU: {
983 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
984
Simon Glass96910ef2025-05-02 08:46:34 -0600985 scene_menu_calc_bbox(menu, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600986 break;
987 }
Simon Glass0023d182023-10-01 19:13:34 -0600988 case SCENEOBJT_TEXTLINE: {
989 struct scene_obj_textline *tline;
990
991 tline = (struct scene_obj_textline *)obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600992 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
993 &bbox[SCENEBB_label]);
Simon Glass0023d182023-10-01 19:13:34 -0600994 break;
995 }
Simon Glassf0994692023-10-01 19:13:29 -0600996 }
997
998 return 0;
999}
1000
Simon Glass7a960052023-06-01 10:22:52 -06001001int scene_calc_dims(struct scene *scn, bool do_menus)
1002{
1003 struct scene_obj *obj;
1004 int ret;
1005
1006 list_for_each_entry(obj, &scn->obj_head, sibling) {
1007 switch (obj->type) {
1008 case SCENEOBJT_NONE:
1009 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -06001010 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -06001011 case SCENEOBJT_TEXTEDIT:
Simon Glass7a960052023-06-01 10:22:52 -06001012 case SCENEOBJT_IMAGE: {
1013 int width;
1014
1015 if (!do_menus) {
1016 ret = scene_obj_get_hw(scn, obj->id, &width);
1017 if (ret < 0)
1018 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -06001019 obj->dims.x = width;
1020 obj->dims.y = ret;
1021 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
1022 obj->bbox.x1 = obj->bbox.x0 + width;
1023 obj->bbox.y1 = obj->bbox.y0 + ret;
1024 obj->flags |= SCENEOF_SIZE_VALID;
1025 }
Simon Glass7a960052023-06-01 10:22:52 -06001026 }
1027 break;
1028 }
1029 case SCENEOBJT_MENU: {
1030 struct scene_obj_menu *menu;
1031
1032 if (do_menus) {
1033 menu = (struct scene_obj_menu *)obj;
1034
1035 ret = scene_menu_calc_dims(menu);
1036 if (ret)
1037 return log_msg_ret("men", ret);
1038 }
1039 break;
1040 }
Simon Glass0023d182023-10-01 19:13:34 -06001041 case SCENEOBJT_TEXTLINE: {
1042 struct scene_obj_textline *tline;
1043
1044 tline = (struct scene_obj_textline *)obj;
1045 ret = scene_textline_calc_dims(tline);
1046 if (ret)
1047 return log_msg_ret("men", ret);
1048
1049 break;
1050 }
Simon Glass7a960052023-06-01 10:22:52 -06001051 }
1052 }
1053
1054 return 0;
1055}
Simon Glassc999e172023-06-01 10:22:53 -06001056
1057int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
1058{
1059 struct scene_obj *obj;
1060 int ret;
1061
1062 /* Avoid error-checking optional items */
1063 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
1064
1065 list_for_each_entry(obj, &scn->obj_head, sibling) {
1066 switch (obj->type) {
1067 case SCENEOBJT_NONE:
1068 case SCENEOBJT_IMAGE:
1069 case SCENEOBJT_MENU:
Simon Glass138a3972025-05-02 08:46:44 -06001070 case SCENEOBJT_BOX:
Simon Glass0023d182023-10-01 19:13:34 -06001071 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -06001072 break;
Simon Glass5dc887d2025-05-02 08:46:46 -06001073 case SCENEOBJT_TEXTEDIT:
1074 scene_txted_set_font(scn, obj->id, NULL,
1075 theme->font_size);
1076 break;
Simon Glassc999e172023-06-01 10:22:53 -06001077 case SCENEOBJT_TEXT:
1078 scene_txt_set_font(scn, obj->id, NULL,
1079 theme->font_size);
1080 break;
1081 }
1082 }
1083
1084 ret = scene_arrange(scn);
1085 if (ret)
1086 return log_msg_ret("arr", ret);
1087
1088 return 0;
1089}
Simon Glass01922ec2023-06-01 10:22:57 -06001090
1091void scene_set_highlight_id(struct scene *scn, uint id)
1092{
1093 scn->highlight_id = id;
1094}
1095
1096void scene_highlight_first(struct scene *scn)
1097{
1098 struct scene_obj *obj;
1099
1100 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -06001101 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -06001102 scene_set_highlight_id(scn, obj->id);
1103 return;
Simon Glass01922ec2023-06-01 10:22:57 -06001104 }
1105 }
1106}
1107
Simon Glassf6a943a2023-10-01 19:13:33 -06001108static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
1109{
1110 int ret;
1111
1112 switch (obj->type) {
1113 case SCENEOBJT_NONE:
1114 case SCENEOBJT_IMAGE:
1115 case SCENEOBJT_MENU:
1116 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -06001117 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -06001118 case SCENEOBJT_TEXTEDIT:
Simon Glassf6a943a2023-10-01 19:13:33 -06001119 break;
1120 case SCENEOBJT_TEXTLINE:
1121 ret = scene_textline_open(scn,
1122 (struct scene_obj_textline *)obj);
1123 if (ret)
1124 return log_msg_ret("op", ret);
1125 break;
1126 }
1127
1128 return 0;
1129}
1130
Simon Glass01922ec2023-06-01 10:22:57 -06001131int scene_set_open(struct scene *scn, uint id, bool open)
1132{
Simon Glassf6a943a2023-10-01 19:13:33 -06001133 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -06001134 int ret;
1135
Simon Glassf6a943a2023-10-01 19:13:33 -06001136 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1137 if (!obj)
1138 return log_msg_ret("find", -ENOENT);
1139
1140 if (open) {
1141 ret = scene_obj_open(scn, obj);
1142 if (ret)
1143 return log_msg_ret("op", ret);
1144 }
1145
Simon Glass01922ec2023-06-01 10:22:57 -06001146 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
1147 open ? SCENEOF_OPEN : 0);
1148 if (ret)
1149 return log_msg_ret("flg", ret);
1150
1151 return 0;
1152}
Simon Glasse90acd82023-08-14 16:40:23 -06001153
1154int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
1155 void *priv)
1156{
1157 struct scene_obj *obj;
1158
1159 list_for_each_entry(obj, &scn->obj_head, sibling) {
1160 int ret;
1161
1162 ret = iter(obj, priv);
1163 if (ret)
1164 return log_msg_ret("itr", ret);
1165 }
1166
1167 return 0;
1168}
Simon Glassf0994692023-10-01 19:13:29 -06001169
Simon Glass96910ef2025-05-02 08:46:34 -06001170int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
1171 struct vidconsole_bbox *dst)
1172{
1173 if (dst->valid) {
1174 dst->x0 = min(dst->x0, src->x0 - inset);
1175 dst->y0 = min(dst->y0, src->y0);
1176 dst->x1 = max(dst->x1, src->x1 + inset);
1177 dst->y1 = max(dst->y1, src->y1);
1178 } else {
1179 dst->x0 = src->x0 - inset;
1180 dst->y0 = src->y0;
1181 dst->x1 = src->x1 + inset;
1182 dst->y1 = src->y1;
1183 dst->valid = true;
1184 }
1185
1186 return 0;
1187}
1188
Simon Glassf0994692023-10-01 19:13:29 -06001189int scene_bbox_union(struct scene *scn, uint id, int inset,
1190 struct vidconsole_bbox *bbox)
1191{
1192 struct scene_obj *obj;
Simon Glass96910ef2025-05-02 08:46:34 -06001193 struct vidconsole_bbox local;
Simon Glassf0994692023-10-01 19:13:29 -06001194
1195 if (!id)
1196 return 0;
1197 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1198 if (!obj)
1199 return log_msg_ret("obj", -ENOENT);
Simon Glass96910ef2025-05-02 08:46:34 -06001200 local.x0 = obj->bbox.x0;
1201 local.y0 = obj->bbox.y0;
1202 local.x1 = obj->bbox.x1;
1203 local.y1 = obj->bbox.y1;
1204 local.valid = true;
1205 scene_bbox_join(&local, inset, bbox);
Simon Glassf0994692023-10-01 19:13:29 -06001206
1207 return 0;
1208}