blob: f971db9aab48078e4a6a6ae52bad20091b49e618 [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 Glass0a4d14b2023-01-06 08:52:37 -0600309int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
310{
Simon Glass6081b0f2023-06-01 10:22:50 -0600311 int ret;
312
313 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
314 hide ? SCENEOF_HIDE : 0);
315 if (ret)
316 return log_msg_ret("flg", ret);
317
318 return 0;
319}
320
321int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
322{
Simon Glass0a4d14b2023-01-06 08:52:37 -0600323 struct scene_obj *obj;
324
325 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
326 if (!obj)
327 return log_msg_ret("find", -ENOENT);
Simon Glass6081b0f2023-06-01 10:22:50 -0600328 obj->flags &= ~clr;
329 obj->flags |= set;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600330
331 return 0;
332}
333
334int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
335{
336 struct scene_obj *obj;
337
338 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
339 if (!obj)
340 return log_msg_ret("find", -ENOENT);
341
342 switch (obj->type) {
343 case SCENEOBJT_NONE:
344 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600345 case SCENEOBJT_TEXTLINE:
Simon Glass138a3972025-05-02 08:46:44 -0600346 case SCENEOBJT_BOX:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600347 break;
348 case SCENEOBJT_IMAGE: {
349 struct scene_obj_img *img = (struct scene_obj_img *)obj;
350 ulong width, height;
351 uint bpix;
352
353 video_bmp_get_info(img->data, &width, &height, &bpix);
354 if (widthp)
355 *widthp = width;
356 return height;
357 }
358 case SCENEOBJT_TEXT: {
Simon Glass9ef02aa2025-05-02 08:46:37 -0600359 struct scene_txt_generic *gen = &((struct scene_obj_txt *)obj)->gen;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600360 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600361 struct vidconsole_bbox bbox;
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600362 int len, ret, limit;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600363 const char *str;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600364
Simon Glass9ef02aa2025-05-02 08:46:37 -0600365 str = expo_get_str(exp, gen->str_id);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600366 if (!str)
367 return log_msg_ret("str", -ENOENT);
368 len = strlen(str);
369
370 /* if there is no console, make it up */
371 if (!exp->cons) {
372 if (widthp)
373 *widthp = 8 * len;
374 return 16;
375 }
376
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600377 limit = obj->flags & SCENEOF_SIZE_VALID ?
378 obj->bbox.x1 - obj->bbox.x0 : -1;
379
Simon Glass9ef02aa2025-05-02 08:46:37 -0600380 ret = vidconsole_measure(scn->expo->cons, gen->font_name,
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600381 gen->font_size, str, limit, &bbox,
382 &gen->lines);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600383 if (ret)
384 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600385 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600386 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600387
Simon Glass9e1a86d2023-06-01 10:22:51 -0600388 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600389 }
390 }
391
392 return 0;
393}
394
395/**
Simon Glass118a7272023-10-01 19:13:30 -0600396 * scene_render_background() - Render the background for an object
397 *
398 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600399 * @box_only: true to show a box around the object, but keep the normal
400 * background colour inside
Simon Glass118a7272023-10-01 19:13:30 -0600401 */
Simon Glass0023d182023-10-01 19:13:34 -0600402static void scene_render_background(struct scene_obj *obj, bool box_only)
Simon Glass118a7272023-10-01 19:13:30 -0600403{
Simon Glass96910ef2025-05-02 08:46:34 -0600404 struct vidconsole_bbox bbox[SCENEBB_count], *sel;
Simon Glass118a7272023-10-01 19:13:30 -0600405 struct expo *exp = obj->scene->expo;
406 const struct expo_theme *theme = &exp->theme;
Simon Glass118a7272023-10-01 19:13:30 -0600407 struct udevice *dev = exp->display;
408 struct video_priv *vid_priv;
409 struct udevice *cons = exp->cons;
410 struct vidconsole_colour old;
411 enum colour_idx fore, back;
412 uint inset = theme->menu_inset;
413
Simon Glass21320da2025-04-02 06:29:33 +1300414 vid_priv = dev_get_uclass_priv(dev);
Simon Glass118a7272023-10-01 19:13:30 -0600415 /* draw a background for the object */
Simon Glass21320da2025-04-02 06:29:33 +1300416 if (vid_priv->white_on_black) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600417 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600418 back = VID_WHITE;
419 } else {
420 fore = VID_LIGHT_GRAY;
421 back = VID_BLACK;
422 }
423
424 /* see if this object wants to render a background */
Simon Glass96910ef2025-05-02 08:46:34 -0600425 if (scene_obj_calc_bbox(obj, bbox))
Simon Glass118a7272023-10-01 19:13:30 -0600426 return;
427
Simon Glass96910ef2025-05-02 08:46:34 -0600428 sel = &bbox[SCENEBB_label];
429 if (!sel->valid)
430 return;
431
Simon Glass118a7272023-10-01 19:13:30 -0600432 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass96910ef2025-05-02 08:46:34 -0600433 video_fill_part(dev, sel->x0 - inset, sel->y0 - inset,
434 sel->x1 + inset, sel->y1 + inset,
Simon Glass118a7272023-10-01 19:13:30 -0600435 vid_priv->colour_fg);
436 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600437 if (box_only) {
Simon Glass96910ef2025-05-02 08:46:34 -0600438 video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1,
Simon Glass0023d182023-10-01 19:13:34 -0600439 vid_priv->colour_bg);
440 }
Simon Glass118a7272023-10-01 19:13:30 -0600441}
442
Simon Glassa841d1a2025-05-02 08:46:38 -0600443static int scene_txt_render(struct expo *exp, struct udevice *dev,
444 struct udevice *cons, struct scene_obj *obj,
445 struct scene_txt_generic *gen, int x, int y,
446 int menu_inset)
447{
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600448 const struct vidconsole_mline *mline;
Simon Glass62f39d22025-05-02 08:46:39 -0600449 struct video_priv *vid_priv;
450 struct vidconsole_colour old;
451 enum colour_idx fore, back;
Simon Glassa841d1a2025-05-02 08:46:38 -0600452 const char *str;
453 int ret;
454
455 if (!cons)
456 return -ENOTSUPP;
457
458 if (gen->font_name || gen->font_size) {
459 ret = vidconsole_select_font(cons, gen->font_name,
460 gen->font_size);
461 } else {
462 ret = vidconsole_select_font(cons, NULL, 0);
463 }
464 if (ret && ret != -ENOSYS)
465 return log_msg_ret("font", ret);
466 str = expo_get_str(exp, gen->str_id);
Simon Glass62f39d22025-05-02 08:46:39 -0600467 if (!str)
468 return 0;
Simon Glassa841d1a2025-05-02 08:46:38 -0600469
Simon Glass62f39d22025-05-02 08:46:39 -0600470 vid_priv = dev_get_uclass_priv(dev);
471 if (vid_priv->white_on_black) {
472 fore = VID_BLACK;
473 back = VID_WHITE;
474 } else {
475 fore = VID_LIGHT_GRAY;
476 back = VID_BLACK;
477 }
Simon Glassa841d1a2025-05-02 08:46:38 -0600478
Simon Glass62f39d22025-05-02 08:46:39 -0600479 if (obj->flags & SCENEOF_POINT) {
480 vidconsole_push_colour(cons, fore, back, &old);
481 video_fill_part(dev, x - menu_inset, y, obj->bbox.x1,
482 obj->bbox.y1, vid_priv->colour_bg);
Simon Glassa841d1a2025-05-02 08:46:38 -0600483 }
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600484
485 if (!gen->lines.count) {
486 vidconsole_set_cursor_pos(cons, x, y);
487 vidconsole_put_string(cons, str);
488 }
489 alist_for_each(mline, &gen->lines) {
490 vidconsole_set_cursor_pos(cons, x + mline->bbox.x0,
491 y + mline->bbox.y0);
492 vidconsole_put_stringn(cons, str + mline->start, mline->len);
493 }
Simon Glass62f39d22025-05-02 08:46:39 -0600494 if (obj->flags & SCENEOF_POINT)
495 vidconsole_pop_colour(cons, &old);
Simon Glassa841d1a2025-05-02 08:46:38 -0600496
497 return 0;
498}
499
Simon Glass118a7272023-10-01 19:13:30 -0600500/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600501 * scene_obj_render() - Render an object
502 *
Simon Glasscfb4f2c2025-05-02 08:46:42 -0600503 * @obj: Object to render
504 * @text_mode: true to use text mode
505 * Return: 0 if OK, -ve on error
Simon Glass0a4d14b2023-01-06 08:52:37 -0600506 */
507static int scene_obj_render(struct scene_obj *obj, bool text_mode)
508{
509 struct scene *scn = obj->scene;
510 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600511 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600512 struct udevice *dev = exp->display;
513 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass138a3972025-05-02 08:46:44 -0600514 struct video_priv *vid_priv;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600515 int x, y, ret;
516
Simon Glassbc3a15f2025-05-02 08:46:31 -0600517 y = obj->bbox.y0;
Simon Glass138a3972025-05-02 08:46:44 -0600518 x = obj->bbox.x0;
519 vid_priv = dev_get_uclass_priv(dev);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600520
521 switch (obj->type) {
522 case SCENEOBJT_NONE:
523 break;
524 case SCENEOBJT_IMAGE: {
525 struct scene_obj_img *img = (struct scene_obj_img *)obj;
526
527 if (!cons)
528 return -ENOTSUPP;
529 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
530 true);
531 if (ret < 0)
532 return log_msg_ret("img", ret);
533 break;
534 }
535 case SCENEOBJT_TEXT: {
Simon Glassa841d1a2025-05-02 08:46:38 -0600536 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600537
Simon Glassa841d1a2025-05-02 08:46:38 -0600538 ret = scene_txt_render(exp, dev, cons, obj, &txt->gen, x, y,
539 theme->menu_inset);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600540 break;
541 }
542 case SCENEOBJT_MENU: {
543 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600544
545 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
546 if (!cons)
547 return -ENOTSUPP;
548
549 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600550 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600551 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600552 /*
553 * With a vidconsole, the text and item pointer are rendered as
554 * normal objects so we don't need to do anything here. The menu
555 * simply controls where they are positioned.
556 */
557 if (cons)
558 return -ENOTSUPP;
559
560 ret = scene_menu_display(menu);
561 if (ret < 0)
562 return log_msg_ret("img", ret);
563
564 break;
565 }
Simon Glass0023d182023-10-01 19:13:34 -0600566 case SCENEOBJT_TEXTLINE:
567 if (obj->flags & SCENEOF_OPEN)
568 scene_render_background(obj, true);
569 break;
Simon Glass138a3972025-05-02 08:46:44 -0600570 case SCENEOBJT_BOX: {
571 struct scene_obj_box *box = (struct scene_obj_box *)obj;
572
573 video_draw_box(dev, obj->bbox.x0, obj->bbox.y0, obj->bbox.x1,
574 obj->bbox.y1, box->width, vid_priv->colour_fg);
575 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600576 }
Simon Glass138a3972025-05-02 08:46:44 -0600577 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600578
579 return 0;
580}
581
Simon Glass377f18e2024-10-14 16:31:55 -0600582int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
583{
584 struct scene_obj *obj;
585
586 arr->label_width = 0;
587 list_for_each_entry(obj, &scn->obj_head, sibling) {
588 uint label_id = 0;
589 int width;
590
591 switch (obj->type) {
592 case SCENEOBJT_NONE:
593 case SCENEOBJT_IMAGE:
594 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600595 case SCENEOBJT_BOX:
Simon Glass377f18e2024-10-14 16:31:55 -0600596 break;
597 case SCENEOBJT_MENU: {
598 struct scene_obj_menu *menu;
599
600 menu = (struct scene_obj_menu *)obj,
601 label_id = menu->title_id;
602 break;
603 }
604 case SCENEOBJT_TEXTLINE: {
605 struct scene_obj_textline *tline;
606
607 tline = (struct scene_obj_textline *)obj,
608 label_id = tline->label_id;
609 break;
610 }
611 }
612
613 if (label_id) {
614 int ret;
615
616 ret = scene_obj_get_hw(scn, label_id, &width);
617 if (ret < 0)
618 return log_msg_ret("hei", ret);
619 arr->label_width = max(arr->label_width, width);
620 }
621 }
622
623 return 0;
624}
625
Simon Glass0a4d14b2023-01-06 08:52:37 -0600626int scene_arrange(struct scene *scn)
627{
Simon Glass377f18e2024-10-14 16:31:55 -0600628 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600629 struct scene_obj *obj;
630 int ret;
631
Simon Glass377f18e2024-10-14 16:31:55 -0600632 ret = scene_calc_arrange(scn, &arr);
633 if (ret < 0)
634 return log_msg_ret("arr", ret);
635
Simon Glass0a4d14b2023-01-06 08:52:37 -0600636 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600637 switch (obj->type) {
638 case SCENEOBJT_NONE:
639 case SCENEOBJT_IMAGE:
640 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600641 case SCENEOBJT_BOX:
Simon Glassb7a64532023-10-01 19:13:24 -0600642 break;
643 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600644 struct scene_obj_menu *menu;
645
646 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600647 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600648 if (ret)
649 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600650 break;
651 }
Simon Glass0023d182023-10-01 19:13:34 -0600652 case SCENEOBJT_TEXTLINE: {
653 struct scene_obj_textline *tline;
654
655 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600656 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600657 if (ret)
658 return log_msg_ret("arr", ret);
659 break;
660 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600661 }
662 }
663
664 return 0;
665}
666
Simon Glass12f57732023-06-01 10:22:58 -0600667int scene_render_deps(struct scene *scn, uint id)
668{
669 struct scene_obj *obj;
670 int ret;
671
672 if (!id)
673 return 0;
674 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
675 if (!obj)
676 return log_msg_ret("obj", -ENOENT);
677
678 if (!(obj->flags & SCENEOF_HIDE)) {
679 ret = scene_obj_render(obj, false);
680 if (ret && ret != -ENOTSUPP)
681 return log_msg_ret("ren", ret);
682
Simon Glassb7a64532023-10-01 19:13:24 -0600683 switch (obj->type) {
684 case SCENEOBJT_NONE:
685 case SCENEOBJT_IMAGE:
686 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600687 case SCENEOBJT_BOX:
Simon Glassb7a64532023-10-01 19:13:24 -0600688 break;
689 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600690 scene_menu_render_deps(scn,
691 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600692 break;
Simon Glass0023d182023-10-01 19:13:34 -0600693 case SCENEOBJT_TEXTLINE:
694 scene_textline_render_deps(scn,
695 (struct scene_obj_textline *)obj);
696 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600697 }
Simon Glass12f57732023-06-01 10:22:58 -0600698 }
699
700 return 0;
701}
702
Simon Glass0a4d14b2023-01-06 08:52:37 -0600703int scene_render(struct scene *scn)
704{
705 struct expo *exp = scn->expo;
706 struct scene_obj *obj;
707 int ret;
708
709 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600710 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600711 ret = scene_obj_render(obj, exp->text_mode);
712 if (ret && ret != -ENOTSUPP)
713 return log_msg_ret("ren", ret);
714 }
715 }
716
Simon Glass12f57732023-06-01 10:22:58 -0600717 /* render any highlighted object on top of the others */
718 if (scn->highlight_id && !exp->text_mode) {
719 ret = scene_render_deps(scn, scn->highlight_id);
720 if (ret && ret != -ENOTSUPP)
721 return log_msg_ret("dep", ret);
722 }
723
Simon Glass0a4d14b2023-01-06 08:52:37 -0600724 return 0;
725}
726
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600727/**
728 * send_key_obj() - Handle a keypress for moving between objects
729 *
730 * @scn: Scene to receive the key
731 * @key: Key to send (KEYCODE_UP)
732 * @event: Returns resulting event from this keypress
733 * Returns: 0 if OK, -ve on error
734 */
735static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
736 struct expo_action *event)
737{
738 switch (key) {
739 case BKEY_UP:
740 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
741 sibling)) {
742 obj = list_entry(obj->sibling.prev,
743 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600744 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600745 event->type = EXPOACT_POINT_OBJ;
746 event->select.id = obj->id;
747 log_debug("up to obj %d\n", event->select.id);
748 break;
749 }
750 }
751 break;
752 case BKEY_DOWN:
753 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
754 obj = list_entry(obj->sibling.next, struct scene_obj,
755 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600756 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600757 event->type = EXPOACT_POINT_OBJ;
758 event->select.id = obj->id;
759 log_debug("down to obj %d\n", event->select.id);
760 break;
761 }
762 }
763 break;
764 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600765 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600766 event->type = EXPOACT_OPEN;
767 event->select.id = obj->id;
768 log_debug("open obj %d\n", event->select.id);
769 }
770 break;
771 case BKEY_QUIT:
772 event->type = EXPOACT_QUIT;
773 log_debug("obj quit\n");
774 break;
775 }
776}
777
Simon Glass0a4d14b2023-01-06 08:52:37 -0600778int scene_send_key(struct scene *scn, int key, struct expo_action *event)
779{
780 struct scene_obj *obj;
781 int ret;
782
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600783 event->type = EXPOACT_NONE;
784
785 /*
786 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
787 * opened
788 */
789 if (scn->expo->popup) {
790 obj = NULL;
791 if (scn->highlight_id) {
792 obj = scene_obj_find(scn, scn->highlight_id,
793 SCENEOBJT_NONE);
794 }
795 if (!obj)
796 return 0;
797
798 if (!(obj->flags & SCENEOF_OPEN)) {
799 send_key_obj(scn, obj, key, event);
800 return 0;
801 }
802
Simon Glassb7a64532023-10-01 19:13:24 -0600803 switch (obj->type) {
804 case SCENEOBJT_NONE:
805 case SCENEOBJT_IMAGE:
806 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600807 case SCENEOBJT_BOX:
Simon Glassb7a64532023-10-01 19:13:24 -0600808 break;
809 case SCENEOBJT_MENU: {
810 struct scene_obj_menu *menu;
811
812 menu = (struct scene_obj_menu *)obj,
813 ret = scene_menu_send_key(scn, menu, key, event);
814 if (ret)
815 return log_msg_ret("key", ret);
816 break;
817 }
Simon Glass0023d182023-10-01 19:13:34 -0600818 case SCENEOBJT_TEXTLINE: {
819 struct scene_obj_textline *tline;
820
821 tline = (struct scene_obj_textline *)obj,
822 ret = scene_textline_send_key(scn, tline, key, event);
823 if (ret)
824 return log_msg_ret("key", ret);
825 break;
826 }
Simon Glassb7a64532023-10-01 19:13:24 -0600827 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600828 return 0;
829 }
830
Simon Glass0a4d14b2023-01-06 08:52:37 -0600831 list_for_each_entry(obj, &scn->obj_head, sibling) {
832 if (obj->type == SCENEOBJT_MENU) {
833 struct scene_obj_menu *menu;
834
835 menu = (struct scene_obj_menu *)obj,
836 ret = scene_menu_send_key(scn, menu, key, event);
837 if (ret)
838 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600839 break;
840 }
841 }
842
843 return 0;
844}
Simon Glass7a960052023-06-01 10:22:52 -0600845
Simon Glass96910ef2025-05-02 08:46:34 -0600846int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
Simon Glassf0994692023-10-01 19:13:29 -0600847{
848 switch (obj->type) {
849 case SCENEOBJT_NONE:
850 case SCENEOBJT_IMAGE:
851 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600852 case SCENEOBJT_BOX:
Simon Glassf0994692023-10-01 19:13:29 -0600853 return -ENOSYS;
854 case SCENEOBJT_MENU: {
855 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
856
Simon Glass96910ef2025-05-02 08:46:34 -0600857 scene_menu_calc_bbox(menu, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600858 break;
859 }
Simon Glass0023d182023-10-01 19:13:34 -0600860 case SCENEOBJT_TEXTLINE: {
861 struct scene_obj_textline *tline;
862
863 tline = (struct scene_obj_textline *)obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600864 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
865 &bbox[SCENEBB_label]);
Simon Glass0023d182023-10-01 19:13:34 -0600866 break;
867 }
Simon Glassf0994692023-10-01 19:13:29 -0600868 }
869
870 return 0;
871}
872
Simon Glass7a960052023-06-01 10:22:52 -0600873int scene_calc_dims(struct scene *scn, bool do_menus)
874{
875 struct scene_obj *obj;
876 int ret;
877
878 list_for_each_entry(obj, &scn->obj_head, sibling) {
879 switch (obj->type) {
880 case SCENEOBJT_NONE:
881 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600882 case SCENEOBJT_BOX:
Simon Glass7a960052023-06-01 10:22:52 -0600883 case SCENEOBJT_IMAGE: {
884 int width;
885
886 if (!do_menus) {
887 ret = scene_obj_get_hw(scn, obj->id, &width);
888 if (ret < 0)
889 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -0600890 obj->dims.x = width;
891 obj->dims.y = ret;
892 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
893 obj->bbox.x1 = obj->bbox.x0 + width;
894 obj->bbox.y1 = obj->bbox.y0 + ret;
895 obj->flags |= SCENEOF_SIZE_VALID;
896 }
Simon Glass7a960052023-06-01 10:22:52 -0600897 }
898 break;
899 }
900 case SCENEOBJT_MENU: {
901 struct scene_obj_menu *menu;
902
903 if (do_menus) {
904 menu = (struct scene_obj_menu *)obj;
905
906 ret = scene_menu_calc_dims(menu);
907 if (ret)
908 return log_msg_ret("men", ret);
909 }
910 break;
911 }
Simon Glass0023d182023-10-01 19:13:34 -0600912 case SCENEOBJT_TEXTLINE: {
913 struct scene_obj_textline *tline;
914
915 tline = (struct scene_obj_textline *)obj;
916 ret = scene_textline_calc_dims(tline);
917 if (ret)
918 return log_msg_ret("men", ret);
919
920 break;
921 }
Simon Glass7a960052023-06-01 10:22:52 -0600922 }
923 }
924
925 return 0;
926}
Simon Glassc999e172023-06-01 10:22:53 -0600927
928int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
929{
930 struct scene_obj *obj;
931 int ret;
932
933 /* Avoid error-checking optional items */
934 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
935
936 list_for_each_entry(obj, &scn->obj_head, sibling) {
937 switch (obj->type) {
938 case SCENEOBJT_NONE:
939 case SCENEOBJT_IMAGE:
940 case SCENEOBJT_MENU:
Simon Glass138a3972025-05-02 08:46:44 -0600941 case SCENEOBJT_BOX:
Simon Glass0023d182023-10-01 19:13:34 -0600942 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600943 break;
944 case SCENEOBJT_TEXT:
945 scene_txt_set_font(scn, obj->id, NULL,
946 theme->font_size);
947 break;
948 }
949 }
950
951 ret = scene_arrange(scn);
952 if (ret)
953 return log_msg_ret("arr", ret);
954
955 return 0;
956}
Simon Glass01922ec2023-06-01 10:22:57 -0600957
958void scene_set_highlight_id(struct scene *scn, uint id)
959{
960 scn->highlight_id = id;
961}
962
963void scene_highlight_first(struct scene *scn)
964{
965 struct scene_obj *obj;
966
967 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600968 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600969 scene_set_highlight_id(scn, obj->id);
970 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600971 }
972 }
973}
974
Simon Glassf6a943a2023-10-01 19:13:33 -0600975static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
976{
977 int ret;
978
979 switch (obj->type) {
980 case SCENEOBJT_NONE:
981 case SCENEOBJT_IMAGE:
982 case SCENEOBJT_MENU:
983 case SCENEOBJT_TEXT:
Simon Glass138a3972025-05-02 08:46:44 -0600984 case SCENEOBJT_BOX:
Simon Glassf6a943a2023-10-01 19:13:33 -0600985 break;
986 case SCENEOBJT_TEXTLINE:
987 ret = scene_textline_open(scn,
988 (struct scene_obj_textline *)obj);
989 if (ret)
990 return log_msg_ret("op", ret);
991 break;
992 }
993
994 return 0;
995}
996
Simon Glass01922ec2023-06-01 10:22:57 -0600997int scene_set_open(struct scene *scn, uint id, bool open)
998{
Simon Glassf6a943a2023-10-01 19:13:33 -0600999 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -06001000 int ret;
1001
Simon Glassf6a943a2023-10-01 19:13:33 -06001002 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1003 if (!obj)
1004 return log_msg_ret("find", -ENOENT);
1005
1006 if (open) {
1007 ret = scene_obj_open(scn, obj);
1008 if (ret)
1009 return log_msg_ret("op", ret);
1010 }
1011
Simon Glass01922ec2023-06-01 10:22:57 -06001012 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
1013 open ? SCENEOF_OPEN : 0);
1014 if (ret)
1015 return log_msg_ret("flg", ret);
1016
1017 return 0;
1018}
Simon Glasse90acd82023-08-14 16:40:23 -06001019
1020int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
1021 void *priv)
1022{
1023 struct scene_obj *obj;
1024
1025 list_for_each_entry(obj, &scn->obj_head, sibling) {
1026 int ret;
1027
1028 ret = iter(obj, priv);
1029 if (ret)
1030 return log_msg_ret("itr", ret);
1031 }
1032
1033 return 0;
1034}
Simon Glassf0994692023-10-01 19:13:29 -06001035
Simon Glass96910ef2025-05-02 08:46:34 -06001036int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
1037 struct vidconsole_bbox *dst)
1038{
1039 if (dst->valid) {
1040 dst->x0 = min(dst->x0, src->x0 - inset);
1041 dst->y0 = min(dst->y0, src->y0);
1042 dst->x1 = max(dst->x1, src->x1 + inset);
1043 dst->y1 = max(dst->y1, src->y1);
1044 } else {
1045 dst->x0 = src->x0 - inset;
1046 dst->y0 = src->y0;
1047 dst->x1 = src->x1 + inset;
1048 dst->y1 = src->y1;
1049 dst->valid = true;
1050 }
1051
1052 return 0;
1053}
1054
Simon Glassf0994692023-10-01 19:13:29 -06001055int scene_bbox_union(struct scene *scn, uint id, int inset,
1056 struct vidconsole_bbox *bbox)
1057{
1058 struct scene_obj *obj;
Simon Glass96910ef2025-05-02 08:46:34 -06001059 struct vidconsole_bbox local;
Simon Glassf0994692023-10-01 19:13:29 -06001060
1061 if (!id)
1062 return 0;
1063 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1064 if (!obj)
1065 return log_msg_ret("obj", -ENOENT);
Simon Glass96910ef2025-05-02 08:46:34 -06001066 local.x0 = obj->bbox.x0;
1067 local.y0 = obj->bbox.y0;
1068 local.x1 = obj->bbox.x1;
1069 local.y1 = obj->bbox.y1;
1070 local.valid = true;
1071 scene_bbox_join(&local, inset, bbox);
Simon Glassf0994692023-10-01 19:13:29 -06001072
1073 return 0;
1074}