blob: fa8f540bfb05c8953fb58f12b723d65402f7fbe3 [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
Simon Glass7bd99772025-05-02 08:46:53 -0600367 if (bbox->x1 == SCENEOB_DISPLAY_MAX)
368 bbox->x1 = xsize ?: 1280;
369 if (bbox->y1 == SCENEOB_DISPLAY_MAX)
370 bbox->y1 = ysize ?: 1024;
371
Simon Glass5beb0572025-05-02 08:46:45 -0600372 width = bbox->x1 - bbox->x0;
373 height = bbox->y1 - bbox->y0;
374
375 switch (horiz) {
376 case SCENEOA_CENTRE:
377 offset->xofs = (width - dims->x) / 2;
378 break;
379 case SCENEOA_RIGHT:
380 offset->xofs = width - dims->x;
381 break;
382 case SCENEOA_LEFT:
383 offset->xofs = 0;
384 break;
385 }
386
387 switch (vert) {
388 case SCENEOA_CENTRE:
389 offset->yofs = (height - dims->y) / 2;
390 break;
391 case SCENEOA_BOTTOM:
392 offset->yofs = height - dims->y;
393 break;
394 case SCENEOA_TOP:
395 default:
396 offset->yofs = 0;
397 break;
398 }
399}
400
Simon Glass0a4d14b2023-01-06 08:52:37 -0600401int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
402{
403 struct scene_obj *obj;
404
405 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
406 if (!obj)
407 return log_msg_ret("find", -ENOENT);
408
409 switch (obj->type) {
410 case SCENEOBJT_NONE:
411 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600412 case SCENEOBJT_TEXTLINE:
Simon Glass138a3972025-05-02 08:46:44 -0600413 case SCENEOBJT_BOX:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600414 break;
415 case SCENEOBJT_IMAGE: {
416 struct scene_obj_img *img = (struct scene_obj_img *)obj;
417 ulong width, height;
418 uint bpix;
419
420 video_bmp_get_info(img->data, &width, &height, &bpix);
421 if (widthp)
422 *widthp = width;
423 return height;
424 }
Simon Glass5dc887d2025-05-02 08:46:46 -0600425 case SCENEOBJT_TEXT:
426 case SCENEOBJT_TEXTEDIT: {
427 struct scene_txt_generic *gen;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600428 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600429 struct vidconsole_bbox bbox;
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600430 int len, ret, limit;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600431 const char *str;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600432
Simon Glass5dc887d2025-05-02 08:46:46 -0600433 if (obj->type == SCENEOBJT_TEXT)
434 gen = &((struct scene_obj_txt *)obj)->gen;
435 else
436 gen = &((struct scene_obj_txtedit *)obj)->gen;
437
Simon Glass9ef02aa2025-05-02 08:46:37 -0600438 str = expo_get_str(exp, gen->str_id);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600439 if (!str)
440 return log_msg_ret("str", -ENOENT);
441 len = strlen(str);
442
443 /* if there is no console, make it up */
444 if (!exp->cons) {
445 if (widthp)
446 *widthp = 8 * len;
447 return 16;
448 }
449
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600450 limit = obj->flags & SCENEOF_SIZE_VALID ?
451 obj->bbox.x1 - obj->bbox.x0 : -1;
452
Simon Glass9ef02aa2025-05-02 08:46:37 -0600453 ret = vidconsole_measure(scn->expo->cons, gen->font_name,
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600454 gen->font_size, str, limit, &bbox,
455 &gen->lines);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600456 if (ret)
457 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600458 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600459 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600460
Simon Glass9e1a86d2023-06-01 10:22:51 -0600461 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600462 }
463 }
464
465 return 0;
466}
467
468/**
Simon Glass118a7272023-10-01 19:13:30 -0600469 * scene_render_background() - Render the background for an object
470 *
471 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600472 * @box_only: true to show a box around the object, but keep the normal
473 * background colour inside
Simon Glass2d857262025-05-02 08:46:50 -0600474 * @cur_item: true to render the background only for the current menu item
Simon Glass118a7272023-10-01 19:13:30 -0600475 */
Simon Glass2d857262025-05-02 08:46:50 -0600476static void scene_render_background(struct scene_obj *obj, bool box_only,
477 bool cur_item)
Simon Glass118a7272023-10-01 19:13:30 -0600478{
Simon Glass96910ef2025-05-02 08:46:34 -0600479 struct vidconsole_bbox bbox[SCENEBB_count], *sel;
Simon Glass118a7272023-10-01 19:13:30 -0600480 struct expo *exp = obj->scene->expo;
481 const struct expo_theme *theme = &exp->theme;
Simon Glass118a7272023-10-01 19:13:30 -0600482 struct udevice *dev = exp->display;
483 struct video_priv *vid_priv;
484 struct udevice *cons = exp->cons;
485 struct vidconsole_colour old;
486 enum colour_idx fore, back;
487 uint inset = theme->menu_inset;
488
Simon Glass21320da2025-04-02 06:29:33 +1300489 vid_priv = dev_get_uclass_priv(dev);
Simon Glass118a7272023-10-01 19:13:30 -0600490 /* draw a background for the object */
Simon Glass21320da2025-04-02 06:29:33 +1300491 if (vid_priv->white_on_black) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600492 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600493 back = VID_WHITE;
494 } else {
495 fore = VID_LIGHT_GRAY;
496 back = VID_BLACK;
497 }
498
499 /* see if this object wants to render a background */
Simon Glass96910ef2025-05-02 08:46:34 -0600500 if (scene_obj_calc_bbox(obj, bbox))
Simon Glass118a7272023-10-01 19:13:30 -0600501 return;
502
Simon Glass2d857262025-05-02 08:46:50 -0600503 sel = cur_item ? &bbox[SCENEBB_curitem] : &bbox[SCENEBB_label];
Simon Glass96910ef2025-05-02 08:46:34 -0600504 if (!sel->valid)
505 return;
506
Simon Glass118a7272023-10-01 19:13:30 -0600507 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass96910ef2025-05-02 08:46:34 -0600508 video_fill_part(dev, sel->x0 - inset, sel->y0 - inset,
509 sel->x1 + inset, sel->y1 + inset,
Simon Glass118a7272023-10-01 19:13:30 -0600510 vid_priv->colour_fg);
511 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600512 if (box_only) {
Simon Glass96910ef2025-05-02 08:46:34 -0600513 video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1,
Simon Glass0023d182023-10-01 19:13:34 -0600514 vid_priv->colour_bg);
515 }
Simon Glass118a7272023-10-01 19:13:30 -0600516}
517
Simon Glassa841d1a2025-05-02 08:46:38 -0600518static int scene_txt_render(struct expo *exp, struct udevice *dev,
519 struct udevice *cons, struct scene_obj *obj,
520 struct scene_txt_generic *gen, int x, int y,
521 int menu_inset)
522{
Simon Glass5beb0572025-05-02 08:46:45 -0600523 const struct vidconsole_mline *mline, *last;
Simon Glass62f39d22025-05-02 08:46:39 -0600524 struct video_priv *vid_priv;
525 struct vidconsole_colour old;
526 enum colour_idx fore, back;
Simon Glass5beb0572025-05-02 08:46:45 -0600527 struct scene_obj_dims dims;
528 struct scene_obj_bbox bbox;
Simon Glassa841d1a2025-05-02 08:46:38 -0600529 const char *str;
530 int ret;
531
532 if (!cons)
533 return -ENOTSUPP;
534
535 if (gen->font_name || gen->font_size) {
536 ret = vidconsole_select_font(cons, gen->font_name,
537 gen->font_size);
538 } else {
539 ret = vidconsole_select_font(cons, NULL, 0);
540 }
541 if (ret && ret != -ENOSYS)
542 return log_msg_ret("font", ret);
543 str = expo_get_str(exp, gen->str_id);
Simon Glass62f39d22025-05-02 08:46:39 -0600544 if (!str)
545 return 0;
Simon Glassa841d1a2025-05-02 08:46:38 -0600546
Simon Glass62f39d22025-05-02 08:46:39 -0600547 vid_priv = dev_get_uclass_priv(dev);
548 if (vid_priv->white_on_black) {
549 fore = VID_BLACK;
550 back = VID_WHITE;
551 } else {
552 fore = VID_LIGHT_GRAY;
553 back = VID_BLACK;
554 }
Simon Glassa841d1a2025-05-02 08:46:38 -0600555
Simon Glass62f39d22025-05-02 08:46:39 -0600556 if (obj->flags & SCENEOF_POINT) {
Simon Glass2d857262025-05-02 08:46:50 -0600557 int inset;
558
559 inset = exp->popup ? menu_inset : 0;
Simon Glass62f39d22025-05-02 08:46:39 -0600560 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass2d857262025-05-02 08:46:50 -0600561 video_fill_part(dev, x - inset, y,
562 obj->bbox.x1, obj->bbox.y1,
563 vid_priv->colour_bg);
Simon Glassa841d1a2025-05-02 08:46:38 -0600564 }
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600565
Simon Glass5beb0572025-05-02 08:46:45 -0600566 mline = alist_get(&gen->lines, 0, typeof(*mline));
567 last = alist_get(&gen->lines, gen->lines.count - 1, typeof(*mline));
568 if (mline)
569 dims.y = last->bbox.y1 - mline->bbox.y0;
570 bbox.y0 = obj->bbox.y0;
571 bbox.y1 = obj->bbox.y1;
572
573 if (!mline) {
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600574 vidconsole_set_cursor_pos(cons, x, y);
575 vidconsole_put_string(cons, str);
576 }
Simon Glass5beb0572025-05-02 08:46:45 -0600577
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600578 alist_for_each(mline, &gen->lines) {
Simon Glass5beb0572025-05-02 08:46:45 -0600579 struct scene_obj_offset offset;
580
581 bbox.x0 = obj->bbox.x0;
582 bbox.x1 = obj->bbox.x1;
583 dims.x = mline->bbox.x1 - mline->bbox.x0;
584 handle_alignment(obj->horiz, obj->vert, &bbox, &dims,
585 obj->bbox.x1 - obj->bbox.x0,
586 obj->bbox.y1 - obj->bbox.y0, &offset);
587
588 x = obj->bbox.x0 + offset.xofs;
589 y = obj->bbox.y0 + offset.yofs + mline->bbox.y0;
590 if (y > bbox.y1)
591 break; /* clip this line and any following */
592 vidconsole_set_cursor_pos(cons, x, y);
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600593 vidconsole_put_stringn(cons, str + mline->start, mline->len);
594 }
Simon Glass62f39d22025-05-02 08:46:39 -0600595 if (obj->flags & SCENEOF_POINT)
596 vidconsole_pop_colour(cons, &old);
Simon Glassa841d1a2025-05-02 08:46:38 -0600597
598 return 0;
599}
600
Simon Glass118a7272023-10-01 19:13:30 -0600601/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600602 * scene_obj_render() - Render an object
603 *
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600604 * @obj: Object to render
605 * @text_mode: true to use text mode
606 * Return: 0 if OK, -ve on error
Simon Glass0a4d14b2023-01-06 08:52:37 -0600607 */
608static int scene_obj_render(struct scene_obj *obj, bool text_mode)
609{
610 struct scene *scn = obj->scene;
611 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600612 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600613 struct udevice *dev = exp->display;
614 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass138a3972025-05-02 08:46:44 -0600615 struct video_priv *vid_priv;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600616 int x, y, ret;
617
Simon Glassbc3a15f2025-05-02 08:46:31 -0600618 y = obj->bbox.y0;
Simon Glass5beb0572025-05-02 08:46:45 -0600619 x = obj->bbox.x0 + obj->ofs.xofs;
Simon Glass138a3972025-05-02 08:46:44 -0600620 vid_priv = dev_get_uclass_priv(dev);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600621
622 switch (obj->type) {
623 case SCENEOBJT_NONE:
624 break;
625 case SCENEOBJT_IMAGE: {
626 struct scene_obj_img *img = (struct scene_obj_img *)obj;
627
628 if (!cons)
629 return -ENOTSUPP;
630 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
631 true);
632 if (ret < 0)
633 return log_msg_ret("img", ret);
634 break;
635 }
636 case SCENEOBJT_TEXT: {
Simon Glassa841d1a2025-05-02 08:46:38 -0600637 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600638
Simon Glassa841d1a2025-05-02 08:46:38 -0600639 ret = scene_txt_render(exp, dev, cons, obj, &txt->gen, x, y,
640 theme->menu_inset);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600641 break;
642 }
643 case SCENEOBJT_MENU: {
644 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600645
Simon Glass2d857262025-05-02 08:46:50 -0600646 if (exp->popup) {
647 if (obj->flags & SCENEOF_OPEN) {
648 if (!cons)
649 return -ENOTSUPP;
Simon Glass01922ec2023-06-01 10:22:57 -0600650
Simon Glass2d857262025-05-02 08:46:50 -0600651 /* draw a background behind the menu items */
652 scene_render_background(obj, false, false);
653 }
654 } else if (exp->show_highlight) {
655 /* do nothing */
Simon Glass01922ec2023-06-01 10:22:57 -0600656 }
Simon Glass2d857262025-05-02 08:46:50 -0600657
Simon Glass0a4d14b2023-01-06 08:52:37 -0600658 /*
659 * With a vidconsole, the text and item pointer are rendered as
660 * normal objects so we don't need to do anything here. The menu
661 * simply controls where they are positioned.
662 */
663 if (cons)
664 return -ENOTSUPP;
665
666 ret = scene_menu_display(menu);
667 if (ret < 0)
668 return log_msg_ret("img", ret);
669
670 break;
671 }
Simon Glass0023d182023-10-01 19:13:34 -0600672 case SCENEOBJT_TEXTLINE:
673 if (obj->flags & SCENEOF_OPEN)
Simon Glass2d857262025-05-02 08:46:50 -0600674 scene_render_background(obj, true, false);
Simon Glass0023d182023-10-01 19:13:34 -0600675 break;
Simon Glass138a3972025-05-02 08:46:44 -0600676 case SCENEOBJT_BOX: {
677 struct scene_obj_box *box = (struct scene_obj_box *)obj;
678
679 video_draw_box(dev, obj->bbox.x0, obj->bbox.y0, obj->bbox.x1,
680 obj->bbox.y1, box->width, vid_priv->colour_fg);
681 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600682 }
Simon Glass5dc887d2025-05-02 08:46:46 -0600683 case SCENEOBJT_TEXTEDIT: {
684 struct scene_obj_txtedit *ted = (struct scene_obj_txtedit *)obj;
685
686 ret = scene_txt_render(exp, dev, cons, obj, &ted->gen, x, y,
687 theme->menu_inset);
688 break;
689 }
Simon Glass138a3972025-05-02 08:46:44 -0600690 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600691
692 return 0;
693}
694
Simon Glass377f18e2024-10-14 16:31:55 -0600695int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
696{
697 struct scene_obj *obj;
698
699 arr->label_width = 0;
700 list_for_each_entry(obj, &scn->obj_head, sibling) {
701 uint label_id = 0;
702 int width;
703
704 switch (obj->type) {
705 case SCENEOBJT_NONE:
706 case SCENEOBJT_IMAGE:
707 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600708 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -0600709 case SCENEOBJT_TEXTEDIT:
Simon Glass377f18e2024-10-14 16:31:55 -0600710 break;
711 case SCENEOBJT_MENU: {
712 struct scene_obj_menu *menu;
713
714 menu = (struct scene_obj_menu *)obj,
715 label_id = menu->title_id;
716 break;
717 }
718 case SCENEOBJT_TEXTLINE: {
719 struct scene_obj_textline *tline;
720
721 tline = (struct scene_obj_textline *)obj,
722 label_id = tline->label_id;
723 break;
724 }
725 }
726
727 if (label_id) {
728 int ret;
729
730 ret = scene_obj_get_hw(scn, label_id, &width);
731 if (ret < 0)
732 return log_msg_ret("hei", ret);
733 arr->label_width = max(arr->label_width, width);
734 }
735 }
736
737 return 0;
738}
739
Simon Glass0a4d14b2023-01-06 08:52:37 -0600740int scene_arrange(struct scene *scn)
741{
Simon Glass377f18e2024-10-14 16:31:55 -0600742 struct expo_arrange_info arr;
Simon Glass5beb0572025-05-02 08:46:45 -0600743 int xsize = 0, ysize = 0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600744 struct scene_obj *obj;
Simon Glass5beb0572025-05-02 08:46:45 -0600745 struct udevice *dev;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600746 int ret;
747
Simon Glass5beb0572025-05-02 08:46:45 -0600748 dev = scn->expo->display;
749 if (dev) {
750 struct video_priv *priv = dev_get_uclass_priv(dev);
751
752 xsize = priv->xsize;
753 ysize = priv->ysize;
754 }
755
Simon Glass377f18e2024-10-14 16:31:55 -0600756 ret = scene_calc_arrange(scn, &arr);
757 if (ret < 0)
758 return log_msg_ret("arr", ret);
759
Simon Glass0a4d14b2023-01-06 08:52:37 -0600760 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass5beb0572025-05-02 08:46:45 -0600761 handle_alignment(obj->horiz, obj->vert, &obj->bbox, &obj->dims,
762 xsize, ysize, &obj->ofs);
763
Simon Glassb7a64532023-10-01 19:13:24 -0600764 switch (obj->type) {
765 case SCENEOBJT_NONE:
766 case SCENEOBJT_IMAGE:
767 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600768 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -0600769 case SCENEOBJT_TEXTEDIT:
Simon Glassb7a64532023-10-01 19:13:24 -0600770 break;
771 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600772 struct scene_obj_menu *menu;
773
774 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600775 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600776 if (ret)
777 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600778 break;
779 }
Simon Glass0023d182023-10-01 19:13:34 -0600780 case SCENEOBJT_TEXTLINE: {
781 struct scene_obj_textline *tline;
782
783 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600784 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600785 if (ret)
786 return log_msg_ret("arr", ret);
787 break;
788 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600789 }
790 }
791
792 return 0;
793}
794
Simon Glass12f57732023-06-01 10:22:58 -0600795int scene_render_deps(struct scene *scn, uint id)
796{
797 struct scene_obj *obj;
798 int ret;
799
800 if (!id)
801 return 0;
802 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
803 if (!obj)
804 return log_msg_ret("obj", -ENOENT);
805
806 if (!(obj->flags & SCENEOF_HIDE)) {
807 ret = scene_obj_render(obj, false);
808 if (ret && ret != -ENOTSUPP)
809 return log_msg_ret("ren", ret);
810
Simon Glassb7a64532023-10-01 19:13:24 -0600811 switch (obj->type) {
812 case SCENEOBJT_NONE:
813 case SCENEOBJT_IMAGE:
814 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600815 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -0600816 case SCENEOBJT_TEXTEDIT:
Simon Glassb7a64532023-10-01 19:13:24 -0600817 break;
818 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600819 scene_menu_render_deps(scn,
820 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600821 break;
Simon Glass0023d182023-10-01 19:13:34 -0600822 case SCENEOBJT_TEXTLINE:
823 scene_textline_render_deps(scn,
824 (struct scene_obj_textline *)obj);
825 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600826 }
Simon Glass12f57732023-06-01 10:22:58 -0600827 }
828
829 return 0;
830}
831
Simon Glass0a4d14b2023-01-06 08:52:37 -0600832int scene_render(struct scene *scn)
833{
834 struct expo *exp = scn->expo;
835 struct scene_obj *obj;
836 int ret;
837
838 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600839 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600840 ret = scene_obj_render(obj, exp->text_mode);
841 if (ret && ret != -ENOTSUPP)
842 return log_msg_ret("ren", ret);
843 }
844 }
845
Simon Glass12f57732023-06-01 10:22:58 -0600846 /* render any highlighted object on top of the others */
847 if (scn->highlight_id && !exp->text_mode) {
848 ret = scene_render_deps(scn, scn->highlight_id);
849 if (ret && ret != -ENOTSUPP)
850 return log_msg_ret("dep", ret);
851 }
852
Simon Glass0a4d14b2023-01-06 08:52:37 -0600853 return 0;
854}
855
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600856/**
857 * send_key_obj() - Handle a keypress for moving between objects
858 *
859 * @scn: Scene to receive the key
860 * @key: Key to send (KEYCODE_UP)
861 * @event: Returns resulting event from this keypress
862 * Returns: 0 if OK, -ve on error
863 */
864static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
865 struct expo_action *event)
866{
867 switch (key) {
868 case BKEY_UP:
869 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
870 sibling)) {
871 obj = list_entry(obj->sibling.prev,
872 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600873 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600874 event->type = EXPOACT_POINT_OBJ;
875 event->select.id = obj->id;
876 log_debug("up to obj %d\n", event->select.id);
877 break;
878 }
879 }
880 break;
881 case BKEY_DOWN:
882 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
883 obj = list_entry(obj->sibling.next, struct scene_obj,
884 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600885 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600886 event->type = EXPOACT_POINT_OBJ;
887 event->select.id = obj->id;
888 log_debug("down to obj %d\n", event->select.id);
889 break;
890 }
891 }
892 break;
893 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600894 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600895 event->type = EXPOACT_OPEN;
896 event->select.id = obj->id;
897 log_debug("open obj %d\n", event->select.id);
898 }
899 break;
900 case BKEY_QUIT:
901 event->type = EXPOACT_QUIT;
902 log_debug("obj quit\n");
903 break;
904 }
905}
906
Simon Glass0a4d14b2023-01-06 08:52:37 -0600907int scene_send_key(struct scene *scn, int key, struct expo_action *event)
908{
909 struct scene_obj *obj;
910 int ret;
911
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600912 event->type = EXPOACT_NONE;
913
914 /*
915 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
916 * opened
917 */
918 if (scn->expo->popup) {
919 obj = NULL;
920 if (scn->highlight_id) {
921 obj = scene_obj_find(scn, scn->highlight_id,
922 SCENEOBJT_NONE);
923 }
924 if (!obj)
925 return 0;
926
927 if (!(obj->flags & SCENEOF_OPEN)) {
928 send_key_obj(scn, obj, key, event);
929 return 0;
930 }
931
Simon Glassb7a64532023-10-01 19:13:24 -0600932 switch (obj->type) {
933 case SCENEOBJT_NONE:
934 case SCENEOBJT_IMAGE:
935 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600936 case SCENEOBJT_BOX:
Simon Glassb7a64532023-10-01 19:13:24 -0600937 break;
938 case SCENEOBJT_MENU: {
939 struct scene_obj_menu *menu;
940
941 menu = (struct scene_obj_menu *)obj,
942 ret = scene_menu_send_key(scn, menu, key, event);
943 if (ret)
944 return log_msg_ret("key", ret);
945 break;
946 }
Simon Glass0023d182023-10-01 19:13:34 -0600947 case SCENEOBJT_TEXTLINE: {
948 struct scene_obj_textline *tline;
949
950 tline = (struct scene_obj_textline *)obj,
951 ret = scene_textline_send_key(scn, tline, key, event);
952 if (ret)
953 return log_msg_ret("key", ret);
954 break;
955 }
Simon Glass5dc887d2025-05-02 08:46:46 -0600956 case SCENEOBJT_TEXTEDIT:
957 /* TODO(sjg@chromium.org): Implement this */
958 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600959 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600960 return 0;
961 }
962
Simon Glass0a4d14b2023-01-06 08:52:37 -0600963 list_for_each_entry(obj, &scn->obj_head, sibling) {
964 if (obj->type == SCENEOBJT_MENU) {
965 struct scene_obj_menu *menu;
966
967 menu = (struct scene_obj_menu *)obj,
968 ret = scene_menu_send_key(scn, menu, key, event);
969 if (ret)
970 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600971 break;
972 }
973 }
974
975 return 0;
976}
Simon Glass7a960052023-06-01 10:22:52 -0600977
Simon Glass96910ef2025-05-02 08:46:34 -0600978int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
Simon Glassf0994692023-10-01 19:13:29 -0600979{
980 switch (obj->type) {
981 case SCENEOBJT_NONE:
982 case SCENEOBJT_IMAGE:
983 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600984 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -0600985 case SCENEOBJT_TEXTEDIT:
Simon Glassf0994692023-10-01 19:13:29 -0600986 return -ENOSYS;
987 case SCENEOBJT_MENU: {
988 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
989
Simon Glass96910ef2025-05-02 08:46:34 -0600990 scene_menu_calc_bbox(menu, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600991 break;
992 }
Simon Glass0023d182023-10-01 19:13:34 -0600993 case SCENEOBJT_TEXTLINE: {
994 struct scene_obj_textline *tline;
995
996 tline = (struct scene_obj_textline *)obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600997 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
998 &bbox[SCENEBB_label]);
Simon Glass0023d182023-10-01 19:13:34 -0600999 break;
1000 }
Simon Glassf0994692023-10-01 19:13:29 -06001001 }
1002
1003 return 0;
1004}
1005
Simon Glass7a960052023-06-01 10:22:52 -06001006int scene_calc_dims(struct scene *scn, bool do_menus)
1007{
1008 struct scene_obj *obj;
1009 int ret;
1010
1011 list_for_each_entry(obj, &scn->obj_head, sibling) {
1012 switch (obj->type) {
1013 case SCENEOBJT_NONE:
1014 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -06001015 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -06001016 case SCENEOBJT_TEXTEDIT:
Simon Glass7a960052023-06-01 10:22:52 -06001017 case SCENEOBJT_IMAGE: {
1018 int width;
1019
1020 if (!do_menus) {
1021 ret = scene_obj_get_hw(scn, obj->id, &width);
1022 if (ret < 0)
1023 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -06001024 obj->dims.x = width;
1025 obj->dims.y = ret;
1026 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
1027 obj->bbox.x1 = obj->bbox.x0 + width;
1028 obj->bbox.y1 = obj->bbox.y0 + ret;
1029 obj->flags |= SCENEOF_SIZE_VALID;
1030 }
Simon Glass7a960052023-06-01 10:22:52 -06001031 }
1032 break;
1033 }
1034 case SCENEOBJT_MENU: {
1035 struct scene_obj_menu *menu;
1036
1037 if (do_menus) {
1038 menu = (struct scene_obj_menu *)obj;
1039
1040 ret = scene_menu_calc_dims(menu);
1041 if (ret)
1042 return log_msg_ret("men", ret);
1043 }
1044 break;
1045 }
Simon Glass0023d182023-10-01 19:13:34 -06001046 case SCENEOBJT_TEXTLINE: {
1047 struct scene_obj_textline *tline;
1048
1049 tline = (struct scene_obj_textline *)obj;
1050 ret = scene_textline_calc_dims(tline);
1051 if (ret)
1052 return log_msg_ret("men", ret);
1053
1054 break;
1055 }
Simon Glass7a960052023-06-01 10:22:52 -06001056 }
1057 }
1058
1059 return 0;
1060}
Simon Glassc999e172023-06-01 10:22:53 -06001061
1062int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
1063{
1064 struct scene_obj *obj;
1065 int ret;
1066
1067 /* Avoid error-checking optional items */
1068 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
1069
1070 list_for_each_entry(obj, &scn->obj_head, sibling) {
1071 switch (obj->type) {
1072 case SCENEOBJT_NONE:
1073 case SCENEOBJT_IMAGE:
1074 case SCENEOBJT_MENU:
Simon Glass138a3972025-05-02 08:46:44 -06001075 case SCENEOBJT_BOX:
Simon Glass0023d182023-10-01 19:13:34 -06001076 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -06001077 break;
Simon Glass5dc887d2025-05-02 08:46:46 -06001078 case SCENEOBJT_TEXTEDIT:
1079 scene_txted_set_font(scn, obj->id, NULL,
1080 theme->font_size);
1081 break;
Simon Glassc999e172023-06-01 10:22:53 -06001082 case SCENEOBJT_TEXT:
1083 scene_txt_set_font(scn, obj->id, NULL,
1084 theme->font_size);
1085 break;
1086 }
1087 }
1088
1089 ret = scene_arrange(scn);
1090 if (ret)
1091 return log_msg_ret("arr", ret);
1092
1093 return 0;
1094}
Simon Glass01922ec2023-06-01 10:22:57 -06001095
1096void scene_set_highlight_id(struct scene *scn, uint id)
1097{
1098 scn->highlight_id = id;
1099}
1100
1101void scene_highlight_first(struct scene *scn)
1102{
1103 struct scene_obj *obj;
1104
1105 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -06001106 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -06001107 scene_set_highlight_id(scn, obj->id);
1108 return;
Simon Glass01922ec2023-06-01 10:22:57 -06001109 }
1110 }
1111}
1112
Simon Glassf6a943a2023-10-01 19:13:33 -06001113static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
1114{
1115 int ret;
1116
1117 switch (obj->type) {
1118 case SCENEOBJT_NONE:
1119 case SCENEOBJT_IMAGE:
1120 case SCENEOBJT_MENU:
1121 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -06001122 case SCENEOBJT_BOX:
Simon Glass5dc887d2025-05-02 08:46:46 -06001123 case SCENEOBJT_TEXTEDIT:
Simon Glassf6a943a2023-10-01 19:13:33 -06001124 break;
1125 case SCENEOBJT_TEXTLINE:
1126 ret = scene_textline_open(scn,
1127 (struct scene_obj_textline *)obj);
1128 if (ret)
1129 return log_msg_ret("op", ret);
1130 break;
1131 }
1132
1133 return 0;
1134}
1135
Simon Glass01922ec2023-06-01 10:22:57 -06001136int scene_set_open(struct scene *scn, uint id, bool open)
1137{
Simon Glassf6a943a2023-10-01 19:13:33 -06001138 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -06001139 int ret;
1140
Simon Glassf6a943a2023-10-01 19:13:33 -06001141 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1142 if (!obj)
1143 return log_msg_ret("find", -ENOENT);
1144
1145 if (open) {
1146 ret = scene_obj_open(scn, obj);
1147 if (ret)
1148 return log_msg_ret("op", ret);
1149 }
1150
Simon Glass01922ec2023-06-01 10:22:57 -06001151 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
1152 open ? SCENEOF_OPEN : 0);
1153 if (ret)
1154 return log_msg_ret("flg", ret);
1155
1156 return 0;
1157}
Simon Glasse90acd82023-08-14 16:40:23 -06001158
1159int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
1160 void *priv)
1161{
1162 struct scene_obj *obj;
1163
1164 list_for_each_entry(obj, &scn->obj_head, sibling) {
1165 int ret;
1166
1167 ret = iter(obj, priv);
1168 if (ret)
1169 return log_msg_ret("itr", ret);
1170 }
1171
1172 return 0;
1173}
Simon Glassf0994692023-10-01 19:13:29 -06001174
Simon Glass96910ef2025-05-02 08:46:34 -06001175int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
1176 struct vidconsole_bbox *dst)
1177{
1178 if (dst->valid) {
1179 dst->x0 = min(dst->x0, src->x0 - inset);
1180 dst->y0 = min(dst->y0, src->y0);
1181 dst->x1 = max(dst->x1, src->x1 + inset);
1182 dst->y1 = max(dst->y1, src->y1);
1183 } else {
1184 dst->x0 = src->x0 - inset;
1185 dst->y0 = src->y0;
1186 dst->x1 = src->x1 + inset;
1187 dst->y1 = src->y1;
1188 dst->valid = true;
1189 }
1190
1191 return 0;
1192}
1193
Simon Glassf0994692023-10-01 19:13:29 -06001194int scene_bbox_union(struct scene *scn, uint id, int inset,
1195 struct vidconsole_bbox *bbox)
1196{
1197 struct scene_obj *obj;
Simon Glass96910ef2025-05-02 08:46:34 -06001198 struct vidconsole_bbox local;
Simon Glassf0994692023-10-01 19:13:29 -06001199
1200 if (!id)
1201 return 0;
1202 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1203 if (!obj)
1204 return log_msg_ret("obj", -ENOENT);
Simon Glass96910ef2025-05-02 08:46:34 -06001205 local.x0 = obj->bbox.x0;
1206 local.y0 = obj->bbox.y0;
1207 local.x1 = obj->bbox.x1;
1208 local.y1 = obj->bbox.y1;
1209 local.valid = true;
1210 scene_bbox_join(&local, inset, bbox);
Simon Glassf0994692023-10-01 19:13:29 -06001211
1212 return 0;
1213}