blob: f1dc2d819228298885ccc4c7b56d20057a2ace3d [file] [log] [blame]
Simon Glass0a4d14b2023-01-06 08:52:37 -06001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Implementation of a scene, a collection of text/image/menu items in an expo
4 *
5 * Copyright 2022 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
Simon Glassfe4c1e22023-06-01 10:22:43 -06009#define LOG_CATEGORY LOGC_EXPO
10
Simon Glass0a4d14b2023-01-06 08:52:37 -060011#include <dm.h>
12#include <expo.h>
13#include <malloc.h>
14#include <mapmem.h>
Simon Glassf0e1e8c2023-06-01 10:22:59 -060015#include <menu.h>
Simon Glass0a4d14b2023-01-06 08:52:37 -060016#include <video.h>
17#include <video_console.h>
18#include <linux/input.h>
19#include "scene_internal.h"
20
Simon Glass0a4d14b2023-01-06 08:52:37 -060021int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
22{
23 struct scene *scn;
24
25 scn = calloc(1, sizeof(struct scene));
26 if (!scn)
27 return log_msg_ret("expo", -ENOMEM);
28 scn->name = strdup(name);
29 if (!scn->name) {
30 free(scn);
31 return log_msg_ret("name", -ENOMEM);
32 }
33
Simon Glass6651e942025-05-01 07:37:01 -060034 if (!abuf_init_size(&scn->buf, EXPO_MAX_CHARS + 1)) {
Simon Glassa968f5f2023-10-01 19:13:31 -060035 free(scn->name);
36 free(scn);
37 return log_msg_ret("buf", -ENOMEM);
38 }
39 abuf_init(&scn->entry_save);
40
Simon Glass0a4d14b2023-01-06 08:52:37 -060041 INIT_LIST_HEAD(&scn->obj_head);
42 scn->id = resolve_id(exp, id);
43 scn->expo = exp;
44 list_add_tail(&scn->sibling, &exp->scene_head);
45
46 *scnp = scn;
47
48 return scn->id;
49}
50
51void scene_obj_destroy(struct scene_obj *obj)
52{
53 if (obj->type == SCENEOBJT_MENU)
54 scene_menu_destroy((struct scene_obj_menu *)obj);
55 free(obj->name);
56 free(obj);
57}
58
59void scene_destroy(struct scene *scn)
60{
61 struct scene_obj *obj, *next;
62
63 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
64 scene_obj_destroy(obj);
65
Simon Glassa968f5f2023-10-01 19:13:31 -060066 abuf_uninit(&scn->entry_save);
67 abuf_uninit(&scn->buf);
Simon Glass0a4d14b2023-01-06 08:52:37 -060068 free(scn->name);
Simon Glass0a4d14b2023-01-06 08:52:37 -060069 free(scn);
70}
71
Simon Glass0a4d14b2023-01-06 08:52:37 -060072int scene_obj_count(struct scene *scn)
73{
Sughosh Ganuebb1c202024-08-28 22:24:22 +053074 return list_count_nodes(&scn->obj_head);
Simon Glass0a4d14b2023-01-06 08:52:37 -060075}
76
Simon Glass45ff0bc2023-08-14 16:40:21 -060077void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
Simon Glass0a4d14b2023-01-06 08:52:37 -060078{
79 struct scene_obj *obj;
80
81 list_for_each_entry(obj, &scn->obj_head, sibling) {
82 if (obj->id == id &&
83 (type == SCENEOBJT_NONE || obj->type == type))
84 return obj;
85 }
86
87 return NULL;
88}
89
Simon Glassc8925112023-06-01 10:23:02 -060090void *scene_obj_find_by_name(struct scene *scn, const char *name)
91{
92 struct scene_obj *obj;
93
94 list_for_each_entry(obj, &scn->obj_head, sibling) {
95 if (!strcmp(name, obj->name))
96 return obj;
97 }
98
99 return NULL;
100}
101
Simon Glass0a4d14b2023-01-06 08:52:37 -0600102int scene_obj_add(struct scene *scn, const char *name, uint id,
103 enum scene_obj_t type, uint size, struct scene_obj **objp)
104{
105 struct scene_obj *obj;
106
107 obj = calloc(1, size);
108 if (!obj)
109 return log_msg_ret("obj", -ENOMEM);
110 obj->name = strdup(name);
111 if (!obj->name) {
112 free(obj);
113 return log_msg_ret("name", -ENOMEM);
114 }
115
116 obj->id = resolve_id(scn->expo, id);
117 obj->scene = scn;
118 obj->type = type;
119 list_add_tail(&obj->sibling, &scn->obj_head);
120 *objp = obj;
121
122 return obj->id;
123}
124
125int scene_img(struct scene *scn, const char *name, uint id, char *data,
126 struct scene_obj_img **imgp)
127{
128 struct scene_obj_img *img;
129 int ret;
130
131 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
132 sizeof(struct scene_obj_img),
133 (struct scene_obj **)&img);
134 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600135 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600136
137 img->data = data;
138
139 if (imgp)
140 *imgp = img;
141
142 return img->obj.id;
143}
144
145int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
146 struct scene_obj_txt **txtp)
147{
148 struct scene_obj_txt *txt;
149 int ret;
150
151 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
152 sizeof(struct scene_obj_txt),
153 (struct scene_obj **)&txt);
154 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600155 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600156
157 txt->str_id = str_id;
158
159 if (txtp)
160 *txtp = txt;
161
162 return txt->obj.id;
163}
164
165int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
166 const char *str, struct scene_obj_txt **txtp)
167{
168 struct scene_obj_txt *txt;
169 int ret;
170
171 ret = expo_str(scn->expo, name, str_id, str);
172 if (ret < 0)
173 return log_msg_ret("str", ret);
Simon Glass51bde1a2023-10-01 19:13:26 -0600174 if (str_id && ret != str_id)
Simon Glass0a4d14b2023-01-06 08:52:37 -0600175 return log_msg_ret("id", -EEXIST);
Simon Glass51bde1a2023-10-01 19:13:26 -0600176 str_id = ret;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600177
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
184 txt->str_id = str_id;
185
186 if (txtp)
187 *txtp = txt;
188
189 return txt->obj.id;
190}
191
192int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
193 uint font_size)
194{
195 struct scene_obj_txt *txt;
196
197 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
198 if (!txt)
199 return log_msg_ret("find", -ENOENT);
200 txt->font_name = font_name;
201 txt->font_size = font_size;
202
203 return 0;
204}
205
206int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
207{
208 struct scene_obj *obj;
Simon Glassebec4972025-05-02 08:46:33 -0600209 int w, h;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600210
211 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
212 if (!obj)
213 return log_msg_ret("find", -ENOENT);
Simon Glassebec4972025-05-02 08:46:33 -0600214 w = obj->bbox.x1 - obj->bbox.x0;
215 h = obj->bbox.y1 - obj->bbox.y0;
Simon Glassbc3a15f2025-05-02 08:46:31 -0600216 obj->bbox.x0 = x;
217 obj->bbox.y0 = y;
Simon Glassebec4972025-05-02 08:46:33 -0600218 obj->bbox.x1 = obj->bbox.x0 + w;
219 obj->bbox.y1 = obj->bbox.y0 + h;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600220
221 return 0;
222}
223
Simon Glass7a960052023-06-01 10:22:52 -0600224int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
225{
226 struct scene_obj *obj;
227
228 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
229 if (!obj)
230 return log_msg_ret("find", -ENOENT);
Simon Glassebec4972025-05-02 08:46:33 -0600231 obj->bbox.x1 = obj->bbox.x0 + w;
232 obj->bbox.y1 = obj->bbox.y0 + h;
233 obj->flags |= SCENEOF_SIZE_VALID;
Simon Glass7a960052023-06-01 10:22:52 -0600234
235 return 0;
236}
237
Simon Glass0a4d14b2023-01-06 08:52:37 -0600238int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
239{
Simon Glass6081b0f2023-06-01 10:22:50 -0600240 int ret;
241
242 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
243 hide ? SCENEOF_HIDE : 0);
244 if (ret)
245 return log_msg_ret("flg", ret);
246
247 return 0;
248}
249
250int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
251{
Simon Glass0a4d14b2023-01-06 08:52:37 -0600252 struct scene_obj *obj;
253
254 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
255 if (!obj)
256 return log_msg_ret("find", -ENOENT);
Simon Glass6081b0f2023-06-01 10:22:50 -0600257 obj->flags &= ~clr;
258 obj->flags |= set;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600259
260 return 0;
261}
262
263int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
264{
265 struct scene_obj *obj;
266
267 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
268 if (!obj)
269 return log_msg_ret("find", -ENOENT);
270
271 switch (obj->type) {
272 case SCENEOBJT_NONE:
273 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600274 case SCENEOBJT_TEXTLINE:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600275 break;
276 case SCENEOBJT_IMAGE: {
277 struct scene_obj_img *img = (struct scene_obj_img *)obj;
278 ulong width, height;
279 uint bpix;
280
281 video_bmp_get_info(img->data, &width, &height, &bpix);
282 if (widthp)
283 *widthp = width;
284 return height;
285 }
286 case SCENEOBJT_TEXT: {
287 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
288 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600289 struct vidconsole_bbox bbox;
290 const char *str;
291 int len, ret;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600292
Simon Glass9e1a86d2023-06-01 10:22:51 -0600293 str = expo_get_str(exp, txt->str_id);
294 if (!str)
295 return log_msg_ret("str", -ENOENT);
296 len = strlen(str);
297
298 /* if there is no console, make it up */
299 if (!exp->cons) {
300 if (widthp)
301 *widthp = 8 * len;
302 return 16;
303 }
304
305 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
Simon Glass3aa33582025-04-02 06:29:39 +1300306 txt->font_size, str, -1, &bbox, NULL);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600307 if (ret)
308 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600309 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600310 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600311
Simon Glass9e1a86d2023-06-01 10:22:51 -0600312 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600313 }
314 }
315
316 return 0;
317}
318
319/**
Simon Glass118a7272023-10-01 19:13:30 -0600320 * scene_render_background() - Render the background for an object
321 *
322 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600323 * @box_only: true to show a box around the object, but keep the normal
324 * background colour inside
Simon Glass118a7272023-10-01 19:13:30 -0600325 */
Simon Glass0023d182023-10-01 19:13:34 -0600326static void scene_render_background(struct scene_obj *obj, bool box_only)
Simon Glass118a7272023-10-01 19:13:30 -0600327{
Simon Glass96910ef2025-05-02 08:46:34 -0600328 struct vidconsole_bbox bbox[SCENEBB_count], *sel;
Simon Glass118a7272023-10-01 19:13:30 -0600329 struct expo *exp = obj->scene->expo;
330 const struct expo_theme *theme = &exp->theme;
Simon Glass118a7272023-10-01 19:13:30 -0600331 struct udevice *dev = exp->display;
332 struct video_priv *vid_priv;
333 struct udevice *cons = exp->cons;
334 struct vidconsole_colour old;
335 enum colour_idx fore, back;
336 uint inset = theme->menu_inset;
337
Simon Glass21320da2025-04-02 06:29:33 +1300338 vid_priv = dev_get_uclass_priv(dev);
Simon Glass118a7272023-10-01 19:13:30 -0600339 /* draw a background for the object */
Simon Glass21320da2025-04-02 06:29:33 +1300340 if (vid_priv->white_on_black) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600341 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600342 back = VID_WHITE;
343 } else {
344 fore = VID_LIGHT_GRAY;
345 back = VID_BLACK;
346 }
347
348 /* see if this object wants to render a background */
Simon Glass96910ef2025-05-02 08:46:34 -0600349 if (scene_obj_calc_bbox(obj, bbox))
Simon Glass118a7272023-10-01 19:13:30 -0600350 return;
351
Simon Glass96910ef2025-05-02 08:46:34 -0600352 sel = &bbox[SCENEBB_label];
353 if (!sel->valid)
354 return;
355
Simon Glass118a7272023-10-01 19:13:30 -0600356 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass96910ef2025-05-02 08:46:34 -0600357 video_fill_part(dev, sel->x0 - inset, sel->y0 - inset,
358 sel->x1 + inset, sel->y1 + inset,
Simon Glass118a7272023-10-01 19:13:30 -0600359 vid_priv->colour_fg);
360 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600361 if (box_only) {
Simon Glass96910ef2025-05-02 08:46:34 -0600362 video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1,
Simon Glass0023d182023-10-01 19:13:34 -0600363 vid_priv->colour_bg);
364 }
Simon Glass118a7272023-10-01 19:13:30 -0600365}
366
367/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600368 * scene_obj_render() - Render an object
369 *
370 */
371static int scene_obj_render(struct scene_obj *obj, bool text_mode)
372{
373 struct scene *scn = obj->scene;
374 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600375 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600376 struct udevice *dev = exp->display;
377 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600378 int x, y, ret;
379
Simon Glassbc3a15f2025-05-02 08:46:31 -0600380 x = obj->bbox.x0;
381 y = obj->bbox.y0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600382
383 switch (obj->type) {
384 case SCENEOBJT_NONE:
385 break;
386 case SCENEOBJT_IMAGE: {
387 struct scene_obj_img *img = (struct scene_obj_img *)obj;
388
389 if (!cons)
390 return -ENOTSUPP;
391 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
392 true);
393 if (ret < 0)
394 return log_msg_ret("img", ret);
395 break;
396 }
397 case SCENEOBJT_TEXT: {
398 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
399 const char *str;
400
401 if (!cons)
402 return -ENOTSUPP;
403
404 if (txt->font_name || txt->font_size) {
405 ret = vidconsole_select_font(cons,
406 txt->font_name,
407 txt->font_size);
408 } else {
409 ret = vidconsole_select_font(cons, NULL, 0);
410 }
411 if (ret && ret != -ENOSYS)
412 return log_msg_ret("font", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600413 str = expo_get_str(exp, txt->str_id);
Simon Glass01922ec2023-06-01 10:22:57 -0600414 if (str) {
415 struct video_priv *vid_priv;
416 struct vidconsole_colour old;
417 enum colour_idx fore, back;
418
Simon Glass21320da2025-04-02 06:29:33 +1300419 vid_priv = dev_get_uclass_priv(dev);
420 if (vid_priv->white_on_black) {
Simon Glass01922ec2023-06-01 10:22:57 -0600421 fore = VID_BLACK;
422 back = VID_WHITE;
423 } else {
424 fore = VID_LIGHT_GRAY;
425 back = VID_BLACK;
426 }
427
Simon Glass01922ec2023-06-01 10:22:57 -0600428 if (obj->flags & SCENEOF_POINT) {
429 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass86f1ac52023-06-01 10:23:00 -0600430 video_fill_part(dev, x - theme->menu_inset, y,
Simon Glassebec4972025-05-02 08:46:33 -0600431 obj->bbox.x1,
432 obj->bbox.y1,
Simon Glass01922ec2023-06-01 10:22:57 -0600433 vid_priv->colour_bg);
434 }
435 vidconsole_set_cursor_pos(cons, x, y);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600436 vidconsole_put_string(cons, str);
Simon Glass01922ec2023-06-01 10:22:57 -0600437 if (obj->flags & SCENEOF_POINT)
438 vidconsole_pop_colour(cons, &old);
439 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600440 break;
441 }
442 case SCENEOBJT_MENU: {
443 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600444
445 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
446 if (!cons)
447 return -ENOTSUPP;
448
449 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600450 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600451 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600452 /*
453 * With a vidconsole, the text and item pointer are rendered as
454 * normal objects so we don't need to do anything here. The menu
455 * simply controls where they are positioned.
456 */
457 if (cons)
458 return -ENOTSUPP;
459
460 ret = scene_menu_display(menu);
461 if (ret < 0)
462 return log_msg_ret("img", ret);
463
464 break;
465 }
Simon Glass0023d182023-10-01 19:13:34 -0600466 case SCENEOBJT_TEXTLINE:
467 if (obj->flags & SCENEOF_OPEN)
468 scene_render_background(obj, true);
469 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600470 }
471
472 return 0;
473}
474
Simon Glass377f18e2024-10-14 16:31:55 -0600475int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
476{
477 struct scene_obj *obj;
478
479 arr->label_width = 0;
480 list_for_each_entry(obj, &scn->obj_head, sibling) {
481 uint label_id = 0;
482 int width;
483
484 switch (obj->type) {
485 case SCENEOBJT_NONE:
486 case SCENEOBJT_IMAGE:
487 case SCENEOBJT_TEXT:
488 break;
489 case SCENEOBJT_MENU: {
490 struct scene_obj_menu *menu;
491
492 menu = (struct scene_obj_menu *)obj,
493 label_id = menu->title_id;
494 break;
495 }
496 case SCENEOBJT_TEXTLINE: {
497 struct scene_obj_textline *tline;
498
499 tline = (struct scene_obj_textline *)obj,
500 label_id = tline->label_id;
501 break;
502 }
503 }
504
505 if (label_id) {
506 int ret;
507
508 ret = scene_obj_get_hw(scn, label_id, &width);
509 if (ret < 0)
510 return log_msg_ret("hei", ret);
511 arr->label_width = max(arr->label_width, width);
512 }
513 }
514
515 return 0;
516}
517
Simon Glass0a4d14b2023-01-06 08:52:37 -0600518int scene_arrange(struct scene *scn)
519{
Simon Glass377f18e2024-10-14 16:31:55 -0600520 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600521 struct scene_obj *obj;
522 int ret;
523
Simon Glass377f18e2024-10-14 16:31:55 -0600524 ret = scene_calc_arrange(scn, &arr);
525 if (ret < 0)
526 return log_msg_ret("arr", ret);
527
Simon Glass0a4d14b2023-01-06 08:52:37 -0600528 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600529 switch (obj->type) {
530 case SCENEOBJT_NONE:
531 case SCENEOBJT_IMAGE:
532 case SCENEOBJT_TEXT:
533 break;
534 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600535 struct scene_obj_menu *menu;
536
537 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600538 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600539 if (ret)
540 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600541 break;
542 }
Simon Glass0023d182023-10-01 19:13:34 -0600543 case SCENEOBJT_TEXTLINE: {
544 struct scene_obj_textline *tline;
545
546 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600547 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600548 if (ret)
549 return log_msg_ret("arr", ret);
550 break;
551 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600552 }
553 }
554
555 return 0;
556}
557
Simon Glass12f57732023-06-01 10:22:58 -0600558int scene_render_deps(struct scene *scn, uint id)
559{
560 struct scene_obj *obj;
561 int ret;
562
563 if (!id)
564 return 0;
565 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
566 if (!obj)
567 return log_msg_ret("obj", -ENOENT);
568
569 if (!(obj->flags & SCENEOF_HIDE)) {
570 ret = scene_obj_render(obj, false);
571 if (ret && ret != -ENOTSUPP)
572 return log_msg_ret("ren", ret);
573
Simon Glassb7a64532023-10-01 19:13:24 -0600574 switch (obj->type) {
575 case SCENEOBJT_NONE:
576 case SCENEOBJT_IMAGE:
577 case SCENEOBJT_TEXT:
578 break;
579 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600580 scene_menu_render_deps(scn,
581 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600582 break;
Simon Glass0023d182023-10-01 19:13:34 -0600583 case SCENEOBJT_TEXTLINE:
584 scene_textline_render_deps(scn,
585 (struct scene_obj_textline *)obj);
586 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600587 }
Simon Glass12f57732023-06-01 10:22:58 -0600588 }
589
590 return 0;
591}
592
Simon Glass0a4d14b2023-01-06 08:52:37 -0600593int scene_render(struct scene *scn)
594{
595 struct expo *exp = scn->expo;
596 struct scene_obj *obj;
597 int ret;
598
599 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600600 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600601 ret = scene_obj_render(obj, exp->text_mode);
602 if (ret && ret != -ENOTSUPP)
603 return log_msg_ret("ren", ret);
604 }
605 }
606
Simon Glass12f57732023-06-01 10:22:58 -0600607 /* render any highlighted object on top of the others */
608 if (scn->highlight_id && !exp->text_mode) {
609 ret = scene_render_deps(scn, scn->highlight_id);
610 if (ret && ret != -ENOTSUPP)
611 return log_msg_ret("dep", ret);
612 }
613
Simon Glass0a4d14b2023-01-06 08:52:37 -0600614 return 0;
615}
616
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600617/**
618 * send_key_obj() - Handle a keypress for moving between objects
619 *
620 * @scn: Scene to receive the key
621 * @key: Key to send (KEYCODE_UP)
622 * @event: Returns resulting event from this keypress
623 * Returns: 0 if OK, -ve on error
624 */
625static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
626 struct expo_action *event)
627{
628 switch (key) {
629 case BKEY_UP:
630 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
631 sibling)) {
632 obj = list_entry(obj->sibling.prev,
633 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600634 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600635 event->type = EXPOACT_POINT_OBJ;
636 event->select.id = obj->id;
637 log_debug("up to obj %d\n", event->select.id);
638 break;
639 }
640 }
641 break;
642 case BKEY_DOWN:
643 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
644 obj = list_entry(obj->sibling.next, struct scene_obj,
645 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600646 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600647 event->type = EXPOACT_POINT_OBJ;
648 event->select.id = obj->id;
649 log_debug("down to obj %d\n", event->select.id);
650 break;
651 }
652 }
653 break;
654 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600655 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600656 event->type = EXPOACT_OPEN;
657 event->select.id = obj->id;
658 log_debug("open obj %d\n", event->select.id);
659 }
660 break;
661 case BKEY_QUIT:
662 event->type = EXPOACT_QUIT;
663 log_debug("obj quit\n");
664 break;
665 }
666}
667
Simon Glass0a4d14b2023-01-06 08:52:37 -0600668int scene_send_key(struct scene *scn, int key, struct expo_action *event)
669{
670 struct scene_obj *obj;
671 int ret;
672
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600673 event->type = EXPOACT_NONE;
674
675 /*
676 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
677 * opened
678 */
679 if (scn->expo->popup) {
680 obj = NULL;
681 if (scn->highlight_id) {
682 obj = scene_obj_find(scn, scn->highlight_id,
683 SCENEOBJT_NONE);
684 }
685 if (!obj)
686 return 0;
687
688 if (!(obj->flags & SCENEOF_OPEN)) {
689 send_key_obj(scn, obj, key, event);
690 return 0;
691 }
692
Simon Glassb7a64532023-10-01 19:13:24 -0600693 switch (obj->type) {
694 case SCENEOBJT_NONE:
695 case SCENEOBJT_IMAGE:
696 case SCENEOBJT_TEXT:
697 break;
698 case SCENEOBJT_MENU: {
699 struct scene_obj_menu *menu;
700
701 menu = (struct scene_obj_menu *)obj,
702 ret = scene_menu_send_key(scn, menu, key, event);
703 if (ret)
704 return log_msg_ret("key", ret);
705 break;
706 }
Simon Glass0023d182023-10-01 19:13:34 -0600707 case SCENEOBJT_TEXTLINE: {
708 struct scene_obj_textline *tline;
709
710 tline = (struct scene_obj_textline *)obj,
711 ret = scene_textline_send_key(scn, tline, key, event);
712 if (ret)
713 return log_msg_ret("key", ret);
714 break;
715 }
Simon Glassb7a64532023-10-01 19:13:24 -0600716 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600717 return 0;
718 }
719
Simon Glass0a4d14b2023-01-06 08:52:37 -0600720 list_for_each_entry(obj, &scn->obj_head, sibling) {
721 if (obj->type == SCENEOBJT_MENU) {
722 struct scene_obj_menu *menu;
723
724 menu = (struct scene_obj_menu *)obj,
725 ret = scene_menu_send_key(scn, menu, key, event);
726 if (ret)
727 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600728 break;
729 }
730 }
731
732 return 0;
733}
Simon Glass7a960052023-06-01 10:22:52 -0600734
Simon Glass96910ef2025-05-02 08:46:34 -0600735int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
Simon Glassf0994692023-10-01 19:13:29 -0600736{
737 switch (obj->type) {
738 case SCENEOBJT_NONE:
739 case SCENEOBJT_IMAGE:
740 case SCENEOBJT_TEXT:
741 return -ENOSYS;
742 case SCENEOBJT_MENU: {
743 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
744
Simon Glass96910ef2025-05-02 08:46:34 -0600745 scene_menu_calc_bbox(menu, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600746 break;
747 }
Simon Glass0023d182023-10-01 19:13:34 -0600748 case SCENEOBJT_TEXTLINE: {
749 struct scene_obj_textline *tline;
750
751 tline = (struct scene_obj_textline *)obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600752 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
753 &bbox[SCENEBB_label]);
Simon Glass0023d182023-10-01 19:13:34 -0600754 break;
755 }
Simon Glassf0994692023-10-01 19:13:29 -0600756 }
757
758 return 0;
759}
760
Simon Glass7a960052023-06-01 10:22:52 -0600761int scene_calc_dims(struct scene *scn, bool do_menus)
762{
763 struct scene_obj *obj;
764 int ret;
765
766 list_for_each_entry(obj, &scn->obj_head, sibling) {
767 switch (obj->type) {
768 case SCENEOBJT_NONE:
769 case SCENEOBJT_TEXT:
770 case SCENEOBJT_IMAGE: {
771 int width;
772
773 if (!do_menus) {
774 ret = scene_obj_get_hw(scn, obj->id, &width);
775 if (ret < 0)
776 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -0600777 obj->dims.x = width;
778 obj->dims.y = ret;
779 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
780 obj->bbox.x1 = obj->bbox.x0 + width;
781 obj->bbox.y1 = obj->bbox.y0 + ret;
782 obj->flags |= SCENEOF_SIZE_VALID;
783 }
Simon Glass7a960052023-06-01 10:22:52 -0600784 }
785 break;
786 }
787 case SCENEOBJT_MENU: {
788 struct scene_obj_menu *menu;
789
790 if (do_menus) {
791 menu = (struct scene_obj_menu *)obj;
792
793 ret = scene_menu_calc_dims(menu);
794 if (ret)
795 return log_msg_ret("men", ret);
796 }
797 break;
798 }
Simon Glass0023d182023-10-01 19:13:34 -0600799 case SCENEOBJT_TEXTLINE: {
800 struct scene_obj_textline *tline;
801
802 tline = (struct scene_obj_textline *)obj;
803 ret = scene_textline_calc_dims(tline);
804 if (ret)
805 return log_msg_ret("men", ret);
806
807 break;
808 }
Simon Glass7a960052023-06-01 10:22:52 -0600809 }
810 }
811
812 return 0;
813}
Simon Glassc999e172023-06-01 10:22:53 -0600814
815int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
816{
817 struct scene_obj *obj;
818 int ret;
819
820 /* Avoid error-checking optional items */
821 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
822
823 list_for_each_entry(obj, &scn->obj_head, sibling) {
824 switch (obj->type) {
825 case SCENEOBJT_NONE:
826 case SCENEOBJT_IMAGE:
827 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600828 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600829 break;
830 case SCENEOBJT_TEXT:
831 scene_txt_set_font(scn, obj->id, NULL,
832 theme->font_size);
833 break;
834 }
835 }
836
837 ret = scene_arrange(scn);
838 if (ret)
839 return log_msg_ret("arr", ret);
840
841 return 0;
842}
Simon Glass01922ec2023-06-01 10:22:57 -0600843
844void scene_set_highlight_id(struct scene *scn, uint id)
845{
846 scn->highlight_id = id;
847}
848
849void scene_highlight_first(struct scene *scn)
850{
851 struct scene_obj *obj;
852
853 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600854 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600855 scene_set_highlight_id(scn, obj->id);
856 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600857 }
858 }
859}
860
Simon Glassf6a943a2023-10-01 19:13:33 -0600861static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
862{
863 int ret;
864
865 switch (obj->type) {
866 case SCENEOBJT_NONE:
867 case SCENEOBJT_IMAGE:
868 case SCENEOBJT_MENU:
869 case SCENEOBJT_TEXT:
870 break;
871 case SCENEOBJT_TEXTLINE:
872 ret = scene_textline_open(scn,
873 (struct scene_obj_textline *)obj);
874 if (ret)
875 return log_msg_ret("op", ret);
876 break;
877 }
878
879 return 0;
880}
881
Simon Glass01922ec2023-06-01 10:22:57 -0600882int scene_set_open(struct scene *scn, uint id, bool open)
883{
Simon Glassf6a943a2023-10-01 19:13:33 -0600884 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600885 int ret;
886
Simon Glassf6a943a2023-10-01 19:13:33 -0600887 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
888 if (!obj)
889 return log_msg_ret("find", -ENOENT);
890
891 if (open) {
892 ret = scene_obj_open(scn, obj);
893 if (ret)
894 return log_msg_ret("op", ret);
895 }
896
Simon Glass01922ec2023-06-01 10:22:57 -0600897 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
898 open ? SCENEOF_OPEN : 0);
899 if (ret)
900 return log_msg_ret("flg", ret);
901
902 return 0;
903}
Simon Glasse90acd82023-08-14 16:40:23 -0600904
905int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
906 void *priv)
907{
908 struct scene_obj *obj;
909
910 list_for_each_entry(obj, &scn->obj_head, sibling) {
911 int ret;
912
913 ret = iter(obj, priv);
914 if (ret)
915 return log_msg_ret("itr", ret);
916 }
917
918 return 0;
919}
Simon Glassf0994692023-10-01 19:13:29 -0600920
Simon Glass96910ef2025-05-02 08:46:34 -0600921int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
922 struct vidconsole_bbox *dst)
923{
924 if (dst->valid) {
925 dst->x0 = min(dst->x0, src->x0 - inset);
926 dst->y0 = min(dst->y0, src->y0);
927 dst->x1 = max(dst->x1, src->x1 + inset);
928 dst->y1 = max(dst->y1, src->y1);
929 } else {
930 dst->x0 = src->x0 - inset;
931 dst->y0 = src->y0;
932 dst->x1 = src->x1 + inset;
933 dst->y1 = src->y1;
934 dst->valid = true;
935 }
936
937 return 0;
938}
939
Simon Glassf0994692023-10-01 19:13:29 -0600940int scene_bbox_union(struct scene *scn, uint id, int inset,
941 struct vidconsole_bbox *bbox)
942{
943 struct scene_obj *obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600944 struct vidconsole_bbox local;
Simon Glassf0994692023-10-01 19:13:29 -0600945
946 if (!id)
947 return 0;
948 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
949 if (!obj)
950 return log_msg_ret("obj", -ENOENT);
Simon Glass96910ef2025-05-02 08:46:34 -0600951 local.x0 = obj->bbox.x0;
952 local.y0 = obj->bbox.y0;
953 local.x1 = obj->bbox.x1;
954 local.y1 = obj->bbox.y1;
955 local.valid = true;
956 scene_bbox_join(&local, inset, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600957
958 return 0;
959}