blob: 325dd23641f4ef75a2b17e84c08ef77f10a67fc7 [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{
328 struct expo *exp = obj->scene->expo;
329 const struct expo_theme *theme = &exp->theme;
330 struct vidconsole_bbox bbox, label_bbox;
331 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 */
349 if (scene_obj_calc_bbox(obj, &bbox, &label_bbox))
350 return;
351
352 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass118a7272023-10-01 19:13:30 -0600353 video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
354 label_bbox.x1 + inset, label_bbox.y1 + inset,
355 vid_priv->colour_fg);
356 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600357 if (box_only) {
358 video_fill_part(dev, label_bbox.x0, label_bbox.y0,
359 label_bbox.x1, label_bbox.y1,
360 vid_priv->colour_bg);
361 }
Simon Glass118a7272023-10-01 19:13:30 -0600362}
363
364/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600365 * scene_obj_render() - Render an object
366 *
367 */
368static int scene_obj_render(struct scene_obj *obj, bool text_mode)
369{
370 struct scene *scn = obj->scene;
371 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600372 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600373 struct udevice *dev = exp->display;
374 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600375 int x, y, ret;
376
Simon Glassbc3a15f2025-05-02 08:46:31 -0600377 x = obj->bbox.x0;
378 y = obj->bbox.y0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600379
380 switch (obj->type) {
381 case SCENEOBJT_NONE:
382 break;
383 case SCENEOBJT_IMAGE: {
384 struct scene_obj_img *img = (struct scene_obj_img *)obj;
385
386 if (!cons)
387 return -ENOTSUPP;
388 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
389 true);
390 if (ret < 0)
391 return log_msg_ret("img", ret);
392 break;
393 }
394 case SCENEOBJT_TEXT: {
395 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
396 const char *str;
397
398 if (!cons)
399 return -ENOTSUPP;
400
401 if (txt->font_name || txt->font_size) {
402 ret = vidconsole_select_font(cons,
403 txt->font_name,
404 txt->font_size);
405 } else {
406 ret = vidconsole_select_font(cons, NULL, 0);
407 }
408 if (ret && ret != -ENOSYS)
409 return log_msg_ret("font", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600410 str = expo_get_str(exp, txt->str_id);
Simon Glass01922ec2023-06-01 10:22:57 -0600411 if (str) {
412 struct video_priv *vid_priv;
413 struct vidconsole_colour old;
414 enum colour_idx fore, back;
415
Simon Glass21320da2025-04-02 06:29:33 +1300416 vid_priv = dev_get_uclass_priv(dev);
417 if (vid_priv->white_on_black) {
Simon Glass01922ec2023-06-01 10:22:57 -0600418 fore = VID_BLACK;
419 back = VID_WHITE;
420 } else {
421 fore = VID_LIGHT_GRAY;
422 back = VID_BLACK;
423 }
424
Simon Glass01922ec2023-06-01 10:22:57 -0600425 if (obj->flags & SCENEOF_POINT) {
426 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass86f1ac52023-06-01 10:23:00 -0600427 video_fill_part(dev, x - theme->menu_inset, y,
Simon Glassebec4972025-05-02 08:46:33 -0600428 obj->bbox.x1,
429 obj->bbox.y1,
Simon Glass01922ec2023-06-01 10:22:57 -0600430 vid_priv->colour_bg);
431 }
432 vidconsole_set_cursor_pos(cons, x, y);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600433 vidconsole_put_string(cons, str);
Simon Glass01922ec2023-06-01 10:22:57 -0600434 if (obj->flags & SCENEOF_POINT)
435 vidconsole_pop_colour(cons, &old);
436 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600437 break;
438 }
439 case SCENEOBJT_MENU: {
440 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600441
442 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
443 if (!cons)
444 return -ENOTSUPP;
445
446 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600447 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600448 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600449 /*
450 * With a vidconsole, the text and item pointer are rendered as
451 * normal objects so we don't need to do anything here. The menu
452 * simply controls where they are positioned.
453 */
454 if (cons)
455 return -ENOTSUPP;
456
457 ret = scene_menu_display(menu);
458 if (ret < 0)
459 return log_msg_ret("img", ret);
460
461 break;
462 }
Simon Glass0023d182023-10-01 19:13:34 -0600463 case SCENEOBJT_TEXTLINE:
464 if (obj->flags & SCENEOF_OPEN)
465 scene_render_background(obj, true);
466 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600467 }
468
469 return 0;
470}
471
Simon Glass377f18e2024-10-14 16:31:55 -0600472int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
473{
474 struct scene_obj *obj;
475
476 arr->label_width = 0;
477 list_for_each_entry(obj, &scn->obj_head, sibling) {
478 uint label_id = 0;
479 int width;
480
481 switch (obj->type) {
482 case SCENEOBJT_NONE:
483 case SCENEOBJT_IMAGE:
484 case SCENEOBJT_TEXT:
485 break;
486 case SCENEOBJT_MENU: {
487 struct scene_obj_menu *menu;
488
489 menu = (struct scene_obj_menu *)obj,
490 label_id = menu->title_id;
491 break;
492 }
493 case SCENEOBJT_TEXTLINE: {
494 struct scene_obj_textline *tline;
495
496 tline = (struct scene_obj_textline *)obj,
497 label_id = tline->label_id;
498 break;
499 }
500 }
501
502 if (label_id) {
503 int ret;
504
505 ret = scene_obj_get_hw(scn, label_id, &width);
506 if (ret < 0)
507 return log_msg_ret("hei", ret);
508 arr->label_width = max(arr->label_width, width);
509 }
510 }
511
512 return 0;
513}
514
Simon Glass0a4d14b2023-01-06 08:52:37 -0600515int scene_arrange(struct scene *scn)
516{
Simon Glass377f18e2024-10-14 16:31:55 -0600517 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600518 struct scene_obj *obj;
519 int ret;
520
Simon Glass377f18e2024-10-14 16:31:55 -0600521 ret = scene_calc_arrange(scn, &arr);
522 if (ret < 0)
523 return log_msg_ret("arr", ret);
524
Simon Glass0a4d14b2023-01-06 08:52:37 -0600525 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600526 switch (obj->type) {
527 case SCENEOBJT_NONE:
528 case SCENEOBJT_IMAGE:
529 case SCENEOBJT_TEXT:
530 break;
531 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600532 struct scene_obj_menu *menu;
533
534 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600535 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600536 if (ret)
537 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600538 break;
539 }
Simon Glass0023d182023-10-01 19:13:34 -0600540 case SCENEOBJT_TEXTLINE: {
541 struct scene_obj_textline *tline;
542
543 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600544 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600545 if (ret)
546 return log_msg_ret("arr", ret);
547 break;
548 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600549 }
550 }
551
552 return 0;
553}
554
Simon Glass12f57732023-06-01 10:22:58 -0600555int scene_render_deps(struct scene *scn, uint id)
556{
557 struct scene_obj *obj;
558 int ret;
559
560 if (!id)
561 return 0;
562 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
563 if (!obj)
564 return log_msg_ret("obj", -ENOENT);
565
566 if (!(obj->flags & SCENEOF_HIDE)) {
567 ret = scene_obj_render(obj, false);
568 if (ret && ret != -ENOTSUPP)
569 return log_msg_ret("ren", ret);
570
Simon Glassb7a64532023-10-01 19:13:24 -0600571 switch (obj->type) {
572 case SCENEOBJT_NONE:
573 case SCENEOBJT_IMAGE:
574 case SCENEOBJT_TEXT:
575 break;
576 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600577 scene_menu_render_deps(scn,
578 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600579 break;
Simon Glass0023d182023-10-01 19:13:34 -0600580 case SCENEOBJT_TEXTLINE:
581 scene_textline_render_deps(scn,
582 (struct scene_obj_textline *)obj);
583 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600584 }
Simon Glass12f57732023-06-01 10:22:58 -0600585 }
586
587 return 0;
588}
589
Simon Glass0a4d14b2023-01-06 08:52:37 -0600590int scene_render(struct scene *scn)
591{
592 struct expo *exp = scn->expo;
593 struct scene_obj *obj;
594 int ret;
595
596 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600597 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600598 ret = scene_obj_render(obj, exp->text_mode);
599 if (ret && ret != -ENOTSUPP)
600 return log_msg_ret("ren", ret);
601 }
602 }
603
Simon Glass12f57732023-06-01 10:22:58 -0600604 /* render any highlighted object on top of the others */
605 if (scn->highlight_id && !exp->text_mode) {
606 ret = scene_render_deps(scn, scn->highlight_id);
607 if (ret && ret != -ENOTSUPP)
608 return log_msg_ret("dep", ret);
609 }
610
Simon Glass0a4d14b2023-01-06 08:52:37 -0600611 return 0;
612}
613
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600614/**
615 * send_key_obj() - Handle a keypress for moving between objects
616 *
617 * @scn: Scene to receive the key
618 * @key: Key to send (KEYCODE_UP)
619 * @event: Returns resulting event from this keypress
620 * Returns: 0 if OK, -ve on error
621 */
622static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
623 struct expo_action *event)
624{
625 switch (key) {
626 case BKEY_UP:
627 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
628 sibling)) {
629 obj = list_entry(obj->sibling.prev,
630 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600631 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600632 event->type = EXPOACT_POINT_OBJ;
633 event->select.id = obj->id;
634 log_debug("up to obj %d\n", event->select.id);
635 break;
636 }
637 }
638 break;
639 case BKEY_DOWN:
640 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
641 obj = list_entry(obj->sibling.next, struct scene_obj,
642 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600643 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600644 event->type = EXPOACT_POINT_OBJ;
645 event->select.id = obj->id;
646 log_debug("down to obj %d\n", event->select.id);
647 break;
648 }
649 }
650 break;
651 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600652 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600653 event->type = EXPOACT_OPEN;
654 event->select.id = obj->id;
655 log_debug("open obj %d\n", event->select.id);
656 }
657 break;
658 case BKEY_QUIT:
659 event->type = EXPOACT_QUIT;
660 log_debug("obj quit\n");
661 break;
662 }
663}
664
Simon Glass0a4d14b2023-01-06 08:52:37 -0600665int scene_send_key(struct scene *scn, int key, struct expo_action *event)
666{
667 struct scene_obj *obj;
668 int ret;
669
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600670 event->type = EXPOACT_NONE;
671
672 /*
673 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
674 * opened
675 */
676 if (scn->expo->popup) {
677 obj = NULL;
678 if (scn->highlight_id) {
679 obj = scene_obj_find(scn, scn->highlight_id,
680 SCENEOBJT_NONE);
681 }
682 if (!obj)
683 return 0;
684
685 if (!(obj->flags & SCENEOF_OPEN)) {
686 send_key_obj(scn, obj, key, event);
687 return 0;
688 }
689
Simon Glassb7a64532023-10-01 19:13:24 -0600690 switch (obj->type) {
691 case SCENEOBJT_NONE:
692 case SCENEOBJT_IMAGE:
693 case SCENEOBJT_TEXT:
694 break;
695 case SCENEOBJT_MENU: {
696 struct scene_obj_menu *menu;
697
698 menu = (struct scene_obj_menu *)obj,
699 ret = scene_menu_send_key(scn, menu, key, event);
700 if (ret)
701 return log_msg_ret("key", ret);
702 break;
703 }
Simon Glass0023d182023-10-01 19:13:34 -0600704 case SCENEOBJT_TEXTLINE: {
705 struct scene_obj_textline *tline;
706
707 tline = (struct scene_obj_textline *)obj,
708 ret = scene_textline_send_key(scn, tline, key, event);
709 if (ret)
710 return log_msg_ret("key", ret);
711 break;
712 }
Simon Glassb7a64532023-10-01 19:13:24 -0600713 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600714 return 0;
715 }
716
Simon Glass0a4d14b2023-01-06 08:52:37 -0600717 list_for_each_entry(obj, &scn->obj_head, sibling) {
718 if (obj->type == SCENEOBJT_MENU) {
719 struct scene_obj_menu *menu;
720
721 menu = (struct scene_obj_menu *)obj,
722 ret = scene_menu_send_key(scn, menu, key, event);
723 if (ret)
724 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600725 break;
726 }
727 }
728
729 return 0;
730}
Simon Glass7a960052023-06-01 10:22:52 -0600731
Simon Glassf0994692023-10-01 19:13:29 -0600732int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
733 struct vidconsole_bbox *label_bbox)
734{
735 switch (obj->type) {
736 case SCENEOBJT_NONE:
737 case SCENEOBJT_IMAGE:
738 case SCENEOBJT_TEXT:
739 return -ENOSYS;
740 case SCENEOBJT_MENU: {
741 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
742
743 scene_menu_calc_bbox(menu, bbox, label_bbox);
744 break;
745 }
Simon Glass0023d182023-10-01 19:13:34 -0600746 case SCENEOBJT_TEXTLINE: {
747 struct scene_obj_textline *tline;
748
749 tline = (struct scene_obj_textline *)obj;
750 scene_textline_calc_bbox(tline, bbox, label_bbox);
751 break;
752 }
Simon Glassf0994692023-10-01 19:13:29 -0600753 }
754
755 return 0;
756}
757
Simon Glass7a960052023-06-01 10:22:52 -0600758int scene_calc_dims(struct scene *scn, bool do_menus)
759{
760 struct scene_obj *obj;
761 int ret;
762
763 list_for_each_entry(obj, &scn->obj_head, sibling) {
764 switch (obj->type) {
765 case SCENEOBJT_NONE:
766 case SCENEOBJT_TEXT:
767 case SCENEOBJT_IMAGE: {
768 int width;
769
770 if (!do_menus) {
771 ret = scene_obj_get_hw(scn, obj->id, &width);
772 if (ret < 0)
773 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -0600774 obj->dims.x = width;
775 obj->dims.y = ret;
776 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
777 obj->bbox.x1 = obj->bbox.x0 + width;
778 obj->bbox.y1 = obj->bbox.y0 + ret;
779 obj->flags |= SCENEOF_SIZE_VALID;
780 }
Simon Glass7a960052023-06-01 10:22:52 -0600781 }
782 break;
783 }
784 case SCENEOBJT_MENU: {
785 struct scene_obj_menu *menu;
786
787 if (do_menus) {
788 menu = (struct scene_obj_menu *)obj;
789
790 ret = scene_menu_calc_dims(menu);
791 if (ret)
792 return log_msg_ret("men", ret);
793 }
794 break;
795 }
Simon Glass0023d182023-10-01 19:13:34 -0600796 case SCENEOBJT_TEXTLINE: {
797 struct scene_obj_textline *tline;
798
799 tline = (struct scene_obj_textline *)obj;
800 ret = scene_textline_calc_dims(tline);
801 if (ret)
802 return log_msg_ret("men", ret);
803
804 break;
805 }
Simon Glass7a960052023-06-01 10:22:52 -0600806 }
807 }
808
809 return 0;
810}
Simon Glassc999e172023-06-01 10:22:53 -0600811
812int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
813{
814 struct scene_obj *obj;
815 int ret;
816
817 /* Avoid error-checking optional items */
818 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
819
820 list_for_each_entry(obj, &scn->obj_head, sibling) {
821 switch (obj->type) {
822 case SCENEOBJT_NONE:
823 case SCENEOBJT_IMAGE:
824 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600825 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600826 break;
827 case SCENEOBJT_TEXT:
828 scene_txt_set_font(scn, obj->id, NULL,
829 theme->font_size);
830 break;
831 }
832 }
833
834 ret = scene_arrange(scn);
835 if (ret)
836 return log_msg_ret("arr", ret);
837
838 return 0;
839}
Simon Glass01922ec2023-06-01 10:22:57 -0600840
841void scene_set_highlight_id(struct scene *scn, uint id)
842{
843 scn->highlight_id = id;
844}
845
846void scene_highlight_first(struct scene *scn)
847{
848 struct scene_obj *obj;
849
850 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600851 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600852 scene_set_highlight_id(scn, obj->id);
853 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600854 }
855 }
856}
857
Simon Glassf6a943a2023-10-01 19:13:33 -0600858static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
859{
860 int ret;
861
862 switch (obj->type) {
863 case SCENEOBJT_NONE:
864 case SCENEOBJT_IMAGE:
865 case SCENEOBJT_MENU:
866 case SCENEOBJT_TEXT:
867 break;
868 case SCENEOBJT_TEXTLINE:
869 ret = scene_textline_open(scn,
870 (struct scene_obj_textline *)obj);
871 if (ret)
872 return log_msg_ret("op", ret);
873 break;
874 }
875
876 return 0;
877}
878
Simon Glass01922ec2023-06-01 10:22:57 -0600879int scene_set_open(struct scene *scn, uint id, bool open)
880{
Simon Glassf6a943a2023-10-01 19:13:33 -0600881 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600882 int ret;
883
Simon Glassf6a943a2023-10-01 19:13:33 -0600884 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
885 if (!obj)
886 return log_msg_ret("find", -ENOENT);
887
888 if (open) {
889 ret = scene_obj_open(scn, obj);
890 if (ret)
891 return log_msg_ret("op", ret);
892 }
893
Simon Glass01922ec2023-06-01 10:22:57 -0600894 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
895 open ? SCENEOF_OPEN : 0);
896 if (ret)
897 return log_msg_ret("flg", ret);
898
899 return 0;
900}
Simon Glasse90acd82023-08-14 16:40:23 -0600901
902int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
903 void *priv)
904{
905 struct scene_obj *obj;
906
907 list_for_each_entry(obj, &scn->obj_head, sibling) {
908 int ret;
909
910 ret = iter(obj, priv);
911 if (ret)
912 return log_msg_ret("itr", ret);
913 }
914
915 return 0;
916}
Simon Glassf0994692023-10-01 19:13:29 -0600917
918int scene_bbox_union(struct scene *scn, uint id, int inset,
919 struct vidconsole_bbox *bbox)
920{
921 struct scene_obj *obj;
922
923 if (!id)
924 return 0;
925 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
926 if (!obj)
927 return log_msg_ret("obj", -ENOENT);
928 if (bbox->valid) {
Simon Glassbc3a15f2025-05-02 08:46:31 -0600929 bbox->x0 = min(bbox->x0, obj->bbox.x0 - inset);
930 bbox->y0 = min(bbox->y0, obj->bbox.y0);
Simon Glassebec4972025-05-02 08:46:33 -0600931 bbox->x1 = max(bbox->x1, obj->bbox.x1 + inset);
932 bbox->y1 = max(bbox->y1, obj->bbox.y1);
Simon Glassf0994692023-10-01 19:13:29 -0600933 } else {
Simon Glassbc3a15f2025-05-02 08:46:31 -0600934 bbox->x0 = obj->bbox.x0 - inset;
935 bbox->y0 = obj->bbox.y0;
Simon Glassebec4972025-05-02 08:46:33 -0600936 bbox->x1 = obj->bbox.x1 + inset;
937 bbox->y1 = obj->bbox.y1;
Simon Glassf0994692023-10-01 19:13:29 -0600938 bbox->valid = true;
939 }
940
941 return 0;
942}