blob: 49dac90ceed3213785c7843f6c29c2e011be46f7 [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;
209
210 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
211 if (!obj)
212 return log_msg_ret("find", -ENOENT);
Simon Glassbc3a15f2025-05-02 08:46:31 -0600213 obj->bbox.x0 = x;
214 obj->bbox.y0 = y;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600215
216 return 0;
217}
218
Simon Glass7a960052023-06-01 10:22:52 -0600219int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
220{
221 struct scene_obj *obj;
222
223 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
224 if (!obj)
225 return log_msg_ret("find", -ENOENT);
Simon Glass854ca692025-05-02 08:46:30 -0600226 obj->bbox.w = w;
227 obj->bbox.h = h;
Simon Glass7a960052023-06-01 10:22:52 -0600228
229 return 0;
230}
231
Simon Glass0a4d14b2023-01-06 08:52:37 -0600232int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
233{
Simon Glass6081b0f2023-06-01 10:22:50 -0600234 int ret;
235
236 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
237 hide ? SCENEOF_HIDE : 0);
238 if (ret)
239 return log_msg_ret("flg", ret);
240
241 return 0;
242}
243
244int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
245{
Simon Glass0a4d14b2023-01-06 08:52:37 -0600246 struct scene_obj *obj;
247
248 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
249 if (!obj)
250 return log_msg_ret("find", -ENOENT);
Simon Glass6081b0f2023-06-01 10:22:50 -0600251 obj->flags &= ~clr;
252 obj->flags |= set;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600253
254 return 0;
255}
256
257int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
258{
259 struct scene_obj *obj;
260
261 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
262 if (!obj)
263 return log_msg_ret("find", -ENOENT);
264
265 switch (obj->type) {
266 case SCENEOBJT_NONE:
267 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600268 case SCENEOBJT_TEXTLINE:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600269 break;
270 case SCENEOBJT_IMAGE: {
271 struct scene_obj_img *img = (struct scene_obj_img *)obj;
272 ulong width, height;
273 uint bpix;
274
275 video_bmp_get_info(img->data, &width, &height, &bpix);
276 if (widthp)
277 *widthp = width;
278 return height;
279 }
280 case SCENEOBJT_TEXT: {
281 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
282 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600283 struct vidconsole_bbox bbox;
284 const char *str;
285 int len, ret;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600286
Simon Glass9e1a86d2023-06-01 10:22:51 -0600287 str = expo_get_str(exp, txt->str_id);
288 if (!str)
289 return log_msg_ret("str", -ENOENT);
290 len = strlen(str);
291
292 /* if there is no console, make it up */
293 if (!exp->cons) {
294 if (widthp)
295 *widthp = 8 * len;
296 return 16;
297 }
298
299 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
Simon Glass3aa33582025-04-02 06:29:39 +1300300 txt->font_size, str, -1, &bbox, NULL);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600301 if (ret)
302 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600303 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600304 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600305
Simon Glass9e1a86d2023-06-01 10:22:51 -0600306 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600307 }
308 }
309
310 return 0;
311}
312
313/**
Simon Glass118a7272023-10-01 19:13:30 -0600314 * scene_render_background() - Render the background for an object
315 *
316 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600317 * @box_only: true to show a box around the object, but keep the normal
318 * background colour inside
Simon Glass118a7272023-10-01 19:13:30 -0600319 */
Simon Glass0023d182023-10-01 19:13:34 -0600320static void scene_render_background(struct scene_obj *obj, bool box_only)
Simon Glass118a7272023-10-01 19:13:30 -0600321{
322 struct expo *exp = obj->scene->expo;
323 const struct expo_theme *theme = &exp->theme;
324 struct vidconsole_bbox bbox, label_bbox;
325 struct udevice *dev = exp->display;
326 struct video_priv *vid_priv;
327 struct udevice *cons = exp->cons;
328 struct vidconsole_colour old;
329 enum colour_idx fore, back;
330 uint inset = theme->menu_inset;
331
Simon Glass21320da2025-04-02 06:29:33 +1300332 vid_priv = dev_get_uclass_priv(dev);
Simon Glass118a7272023-10-01 19:13:30 -0600333 /* draw a background for the object */
Simon Glass21320da2025-04-02 06:29:33 +1300334 if (vid_priv->white_on_black) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600335 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600336 back = VID_WHITE;
337 } else {
338 fore = VID_LIGHT_GRAY;
339 back = VID_BLACK;
340 }
341
342 /* see if this object wants to render a background */
343 if (scene_obj_calc_bbox(obj, &bbox, &label_bbox))
344 return;
345
346 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass118a7272023-10-01 19:13:30 -0600347 video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
348 label_bbox.x1 + inset, label_bbox.y1 + inset,
349 vid_priv->colour_fg);
350 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600351 if (box_only) {
352 video_fill_part(dev, label_bbox.x0, label_bbox.y0,
353 label_bbox.x1, label_bbox.y1,
354 vid_priv->colour_bg);
355 }
Simon Glass118a7272023-10-01 19:13:30 -0600356}
357
358/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600359 * scene_obj_render() - Render an object
360 *
361 */
362static int scene_obj_render(struct scene_obj *obj, bool text_mode)
363{
364 struct scene *scn = obj->scene;
365 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600366 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600367 struct udevice *dev = exp->display;
368 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600369 int x, y, ret;
370
Simon Glassbc3a15f2025-05-02 08:46:31 -0600371 x = obj->bbox.x0;
372 y = obj->bbox.y0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600373
374 switch (obj->type) {
375 case SCENEOBJT_NONE:
376 break;
377 case SCENEOBJT_IMAGE: {
378 struct scene_obj_img *img = (struct scene_obj_img *)obj;
379
380 if (!cons)
381 return -ENOTSUPP;
382 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
383 true);
384 if (ret < 0)
385 return log_msg_ret("img", ret);
386 break;
387 }
388 case SCENEOBJT_TEXT: {
389 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
390 const char *str;
391
392 if (!cons)
393 return -ENOTSUPP;
394
395 if (txt->font_name || txt->font_size) {
396 ret = vidconsole_select_font(cons,
397 txt->font_name,
398 txt->font_size);
399 } else {
400 ret = vidconsole_select_font(cons, NULL, 0);
401 }
402 if (ret && ret != -ENOSYS)
403 return log_msg_ret("font", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600404 str = expo_get_str(exp, txt->str_id);
Simon Glass01922ec2023-06-01 10:22:57 -0600405 if (str) {
406 struct video_priv *vid_priv;
407 struct vidconsole_colour old;
408 enum colour_idx fore, back;
409
Simon Glass21320da2025-04-02 06:29:33 +1300410 vid_priv = dev_get_uclass_priv(dev);
411 if (vid_priv->white_on_black) {
Simon Glass01922ec2023-06-01 10:22:57 -0600412 fore = VID_BLACK;
413 back = VID_WHITE;
414 } else {
415 fore = VID_LIGHT_GRAY;
416 back = VID_BLACK;
417 }
418
Simon Glass01922ec2023-06-01 10:22:57 -0600419 if (obj->flags & SCENEOF_POINT) {
420 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass86f1ac52023-06-01 10:23:00 -0600421 video_fill_part(dev, x - theme->menu_inset, y,
Simon Glass854ca692025-05-02 08:46:30 -0600422 x + obj->bbox.w,
423 y + obj->bbox.h,
Simon Glass01922ec2023-06-01 10:22:57 -0600424 vid_priv->colour_bg);
425 }
426 vidconsole_set_cursor_pos(cons, x, y);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600427 vidconsole_put_string(cons, str);
Simon Glass01922ec2023-06-01 10:22:57 -0600428 if (obj->flags & SCENEOF_POINT)
429 vidconsole_pop_colour(cons, &old);
430 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600431 break;
432 }
433 case SCENEOBJT_MENU: {
434 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600435
436 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
437 if (!cons)
438 return -ENOTSUPP;
439
440 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600441 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600442 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600443 /*
444 * With a vidconsole, the text and item pointer are rendered as
445 * normal objects so we don't need to do anything here. The menu
446 * simply controls where they are positioned.
447 */
448 if (cons)
449 return -ENOTSUPP;
450
451 ret = scene_menu_display(menu);
452 if (ret < 0)
453 return log_msg_ret("img", ret);
454
455 break;
456 }
Simon Glass0023d182023-10-01 19:13:34 -0600457 case SCENEOBJT_TEXTLINE:
458 if (obj->flags & SCENEOF_OPEN)
459 scene_render_background(obj, true);
460 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600461 }
462
463 return 0;
464}
465
Simon Glass377f18e2024-10-14 16:31:55 -0600466int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
467{
468 struct scene_obj *obj;
469
470 arr->label_width = 0;
471 list_for_each_entry(obj, &scn->obj_head, sibling) {
472 uint label_id = 0;
473 int width;
474
475 switch (obj->type) {
476 case SCENEOBJT_NONE:
477 case SCENEOBJT_IMAGE:
478 case SCENEOBJT_TEXT:
479 break;
480 case SCENEOBJT_MENU: {
481 struct scene_obj_menu *menu;
482
483 menu = (struct scene_obj_menu *)obj,
484 label_id = menu->title_id;
485 break;
486 }
487 case SCENEOBJT_TEXTLINE: {
488 struct scene_obj_textline *tline;
489
490 tline = (struct scene_obj_textline *)obj,
491 label_id = tline->label_id;
492 break;
493 }
494 }
495
496 if (label_id) {
497 int ret;
498
499 ret = scene_obj_get_hw(scn, label_id, &width);
500 if (ret < 0)
501 return log_msg_ret("hei", ret);
502 arr->label_width = max(arr->label_width, width);
503 }
504 }
505
506 return 0;
507}
508
Simon Glass0a4d14b2023-01-06 08:52:37 -0600509int scene_arrange(struct scene *scn)
510{
Simon Glass377f18e2024-10-14 16:31:55 -0600511 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600512 struct scene_obj *obj;
513 int ret;
514
Simon Glass377f18e2024-10-14 16:31:55 -0600515 ret = scene_calc_arrange(scn, &arr);
516 if (ret < 0)
517 return log_msg_ret("arr", ret);
518
Simon Glass0a4d14b2023-01-06 08:52:37 -0600519 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600520 switch (obj->type) {
521 case SCENEOBJT_NONE:
522 case SCENEOBJT_IMAGE:
523 case SCENEOBJT_TEXT:
524 break;
525 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600526 struct scene_obj_menu *menu;
527
528 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600529 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600530 if (ret)
531 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600532 break;
533 }
Simon Glass0023d182023-10-01 19:13:34 -0600534 case SCENEOBJT_TEXTLINE: {
535 struct scene_obj_textline *tline;
536
537 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600538 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600539 if (ret)
540 return log_msg_ret("arr", ret);
541 break;
542 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600543 }
544 }
545
546 return 0;
547}
548
Simon Glass12f57732023-06-01 10:22:58 -0600549int scene_render_deps(struct scene *scn, uint id)
550{
551 struct scene_obj *obj;
552 int ret;
553
554 if (!id)
555 return 0;
556 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
557 if (!obj)
558 return log_msg_ret("obj", -ENOENT);
559
560 if (!(obj->flags & SCENEOF_HIDE)) {
561 ret = scene_obj_render(obj, false);
562 if (ret && ret != -ENOTSUPP)
563 return log_msg_ret("ren", ret);
564
Simon Glassb7a64532023-10-01 19:13:24 -0600565 switch (obj->type) {
566 case SCENEOBJT_NONE:
567 case SCENEOBJT_IMAGE:
568 case SCENEOBJT_TEXT:
569 break;
570 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600571 scene_menu_render_deps(scn,
572 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600573 break;
Simon Glass0023d182023-10-01 19:13:34 -0600574 case SCENEOBJT_TEXTLINE:
575 scene_textline_render_deps(scn,
576 (struct scene_obj_textline *)obj);
577 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600578 }
Simon Glass12f57732023-06-01 10:22:58 -0600579 }
580
581 return 0;
582}
583
Simon Glass0a4d14b2023-01-06 08:52:37 -0600584int scene_render(struct scene *scn)
585{
586 struct expo *exp = scn->expo;
587 struct scene_obj *obj;
588 int ret;
589
590 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600591 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600592 ret = scene_obj_render(obj, exp->text_mode);
593 if (ret && ret != -ENOTSUPP)
594 return log_msg_ret("ren", ret);
595 }
596 }
597
Simon Glass12f57732023-06-01 10:22:58 -0600598 /* render any highlighted object on top of the others */
599 if (scn->highlight_id && !exp->text_mode) {
600 ret = scene_render_deps(scn, scn->highlight_id);
601 if (ret && ret != -ENOTSUPP)
602 return log_msg_ret("dep", ret);
603 }
604
Simon Glass0a4d14b2023-01-06 08:52:37 -0600605 return 0;
606}
607
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600608/**
609 * send_key_obj() - Handle a keypress for moving between objects
610 *
611 * @scn: Scene to receive the key
612 * @key: Key to send (KEYCODE_UP)
613 * @event: Returns resulting event from this keypress
614 * Returns: 0 if OK, -ve on error
615 */
616static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
617 struct expo_action *event)
618{
619 switch (key) {
620 case BKEY_UP:
621 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
622 sibling)) {
623 obj = list_entry(obj->sibling.prev,
624 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600625 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600626 event->type = EXPOACT_POINT_OBJ;
627 event->select.id = obj->id;
628 log_debug("up to obj %d\n", event->select.id);
629 break;
630 }
631 }
632 break;
633 case BKEY_DOWN:
634 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
635 obj = list_entry(obj->sibling.next, struct scene_obj,
636 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600637 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600638 event->type = EXPOACT_POINT_OBJ;
639 event->select.id = obj->id;
640 log_debug("down to obj %d\n", event->select.id);
641 break;
642 }
643 }
644 break;
645 case BKEY_SELECT:
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_OPEN;
648 event->select.id = obj->id;
649 log_debug("open obj %d\n", event->select.id);
650 }
651 break;
652 case BKEY_QUIT:
653 event->type = EXPOACT_QUIT;
654 log_debug("obj quit\n");
655 break;
656 }
657}
658
Simon Glass0a4d14b2023-01-06 08:52:37 -0600659int scene_send_key(struct scene *scn, int key, struct expo_action *event)
660{
661 struct scene_obj *obj;
662 int ret;
663
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600664 event->type = EXPOACT_NONE;
665
666 /*
667 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
668 * opened
669 */
670 if (scn->expo->popup) {
671 obj = NULL;
672 if (scn->highlight_id) {
673 obj = scene_obj_find(scn, scn->highlight_id,
674 SCENEOBJT_NONE);
675 }
676 if (!obj)
677 return 0;
678
679 if (!(obj->flags & SCENEOF_OPEN)) {
680 send_key_obj(scn, obj, key, event);
681 return 0;
682 }
683
Simon Glassb7a64532023-10-01 19:13:24 -0600684 switch (obj->type) {
685 case SCENEOBJT_NONE:
686 case SCENEOBJT_IMAGE:
687 case SCENEOBJT_TEXT:
688 break;
689 case SCENEOBJT_MENU: {
690 struct scene_obj_menu *menu;
691
692 menu = (struct scene_obj_menu *)obj,
693 ret = scene_menu_send_key(scn, menu, key, event);
694 if (ret)
695 return log_msg_ret("key", ret);
696 break;
697 }
Simon Glass0023d182023-10-01 19:13:34 -0600698 case SCENEOBJT_TEXTLINE: {
699 struct scene_obj_textline *tline;
700
701 tline = (struct scene_obj_textline *)obj,
702 ret = scene_textline_send_key(scn, tline, key, event);
703 if (ret)
704 return log_msg_ret("key", ret);
705 break;
706 }
Simon Glassb7a64532023-10-01 19:13:24 -0600707 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600708 return 0;
709 }
710
Simon Glass0a4d14b2023-01-06 08:52:37 -0600711 list_for_each_entry(obj, &scn->obj_head, sibling) {
712 if (obj->type == SCENEOBJT_MENU) {
713 struct scene_obj_menu *menu;
714
715 menu = (struct scene_obj_menu *)obj,
716 ret = scene_menu_send_key(scn, menu, key, event);
717 if (ret)
718 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600719 break;
720 }
721 }
722
723 return 0;
724}
Simon Glass7a960052023-06-01 10:22:52 -0600725
Simon Glassf0994692023-10-01 19:13:29 -0600726int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
727 struct vidconsole_bbox *label_bbox)
728{
729 switch (obj->type) {
730 case SCENEOBJT_NONE:
731 case SCENEOBJT_IMAGE:
732 case SCENEOBJT_TEXT:
733 return -ENOSYS;
734 case SCENEOBJT_MENU: {
735 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
736
737 scene_menu_calc_bbox(menu, bbox, label_bbox);
738 break;
739 }
Simon Glass0023d182023-10-01 19:13:34 -0600740 case SCENEOBJT_TEXTLINE: {
741 struct scene_obj_textline *tline;
742
743 tline = (struct scene_obj_textline *)obj;
744 scene_textline_calc_bbox(tline, bbox, label_bbox);
745 break;
746 }
Simon Glassf0994692023-10-01 19:13:29 -0600747 }
748
749 return 0;
750}
751
Simon Glass7a960052023-06-01 10:22:52 -0600752int scene_calc_dims(struct scene *scn, bool do_menus)
753{
754 struct scene_obj *obj;
755 int ret;
756
757 list_for_each_entry(obj, &scn->obj_head, sibling) {
758 switch (obj->type) {
759 case SCENEOBJT_NONE:
760 case SCENEOBJT_TEXT:
761 case SCENEOBJT_IMAGE: {
762 int width;
763
764 if (!do_menus) {
765 ret = scene_obj_get_hw(scn, obj->id, &width);
766 if (ret < 0)
767 return log_msg_ret("get", ret);
Simon Glass854ca692025-05-02 08:46:30 -0600768 obj->bbox.w = width;
769 obj->bbox.h = ret;
Simon Glass7a960052023-06-01 10:22:52 -0600770 }
771 break;
772 }
773 case SCENEOBJT_MENU: {
774 struct scene_obj_menu *menu;
775
776 if (do_menus) {
777 menu = (struct scene_obj_menu *)obj;
778
779 ret = scene_menu_calc_dims(menu);
780 if (ret)
781 return log_msg_ret("men", ret);
782 }
783 break;
784 }
Simon Glass0023d182023-10-01 19:13:34 -0600785 case SCENEOBJT_TEXTLINE: {
786 struct scene_obj_textline *tline;
787
788 tline = (struct scene_obj_textline *)obj;
789 ret = scene_textline_calc_dims(tline);
790 if (ret)
791 return log_msg_ret("men", ret);
792
793 break;
794 }
Simon Glass7a960052023-06-01 10:22:52 -0600795 }
796 }
797
798 return 0;
799}
Simon Glassc999e172023-06-01 10:22:53 -0600800
801int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
802{
803 struct scene_obj *obj;
804 int ret;
805
806 /* Avoid error-checking optional items */
807 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
808
809 list_for_each_entry(obj, &scn->obj_head, sibling) {
810 switch (obj->type) {
811 case SCENEOBJT_NONE:
812 case SCENEOBJT_IMAGE:
813 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600814 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600815 break;
816 case SCENEOBJT_TEXT:
817 scene_txt_set_font(scn, obj->id, NULL,
818 theme->font_size);
819 break;
820 }
821 }
822
823 ret = scene_arrange(scn);
824 if (ret)
825 return log_msg_ret("arr", ret);
826
827 return 0;
828}
Simon Glass01922ec2023-06-01 10:22:57 -0600829
830void scene_set_highlight_id(struct scene *scn, uint id)
831{
832 scn->highlight_id = id;
833}
834
835void scene_highlight_first(struct scene *scn)
836{
837 struct scene_obj *obj;
838
839 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600840 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600841 scene_set_highlight_id(scn, obj->id);
842 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600843 }
844 }
845}
846
Simon Glassf6a943a2023-10-01 19:13:33 -0600847static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
848{
849 int ret;
850
851 switch (obj->type) {
852 case SCENEOBJT_NONE:
853 case SCENEOBJT_IMAGE:
854 case SCENEOBJT_MENU:
855 case SCENEOBJT_TEXT:
856 break;
857 case SCENEOBJT_TEXTLINE:
858 ret = scene_textline_open(scn,
859 (struct scene_obj_textline *)obj);
860 if (ret)
861 return log_msg_ret("op", ret);
862 break;
863 }
864
865 return 0;
866}
867
Simon Glass01922ec2023-06-01 10:22:57 -0600868int scene_set_open(struct scene *scn, uint id, bool open)
869{
Simon Glassf6a943a2023-10-01 19:13:33 -0600870 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600871 int ret;
872
Simon Glassf6a943a2023-10-01 19:13:33 -0600873 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
874 if (!obj)
875 return log_msg_ret("find", -ENOENT);
876
877 if (open) {
878 ret = scene_obj_open(scn, obj);
879 if (ret)
880 return log_msg_ret("op", ret);
881 }
882
Simon Glass01922ec2023-06-01 10:22:57 -0600883 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
884 open ? SCENEOF_OPEN : 0);
885 if (ret)
886 return log_msg_ret("flg", ret);
887
888 return 0;
889}
Simon Glasse90acd82023-08-14 16:40:23 -0600890
891int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
892 void *priv)
893{
894 struct scene_obj *obj;
895
896 list_for_each_entry(obj, &scn->obj_head, sibling) {
897 int ret;
898
899 ret = iter(obj, priv);
900 if (ret)
901 return log_msg_ret("itr", ret);
902 }
903
904 return 0;
905}
Simon Glassf0994692023-10-01 19:13:29 -0600906
907int scene_bbox_union(struct scene *scn, uint id, int inset,
908 struct vidconsole_bbox *bbox)
909{
910 struct scene_obj *obj;
911
912 if (!id)
913 return 0;
914 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
915 if (!obj)
916 return log_msg_ret("obj", -ENOENT);
917 if (bbox->valid) {
Simon Glassbc3a15f2025-05-02 08:46:31 -0600918 bbox->x0 = min(bbox->x0, obj->bbox.x0 - inset);
919 bbox->y0 = min(bbox->y0, obj->bbox.y0);
920 bbox->x1 = max(bbox->x1, obj->bbox.x0 + obj->bbox.w + inset);
921 bbox->y1 = max(bbox->y1, obj->bbox.y0 + obj->bbox.h);
Simon Glassf0994692023-10-01 19:13:29 -0600922 } else {
Simon Glassbc3a15f2025-05-02 08:46:31 -0600923 bbox->x0 = obj->bbox.x0 - inset;
924 bbox->y0 = obj->bbox.y0;
925 bbox->x1 = obj->bbox.x0 + obj->bbox.w + inset;
926 bbox->y1 = obj->bbox.y0 + obj->bbox.h;
Simon Glassf0994692023-10-01 19:13:29 -0600927 bbox->valid = true;
928 }
929
930 return 0;
931}