blob: 08ed85e9188cdd494ea531e40950eff4d72422db [file] [log] [blame]
Simon Glass5e2607a2023-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 Glassc98cb512023-06-01 10:22:43 -06009#define LOG_CATEGORY LOGC_EXPO
10
Simon Glass5e2607a2023-01-06 08:52:37 -060011#include <common.h>
12#include <dm.h>
13#include <expo.h>
14#include <malloc.h>
15#include <mapmem.h>
Simon Glass4e64bee2023-06-01 10:22:59 -060016#include <menu.h>
Simon Glass5e2607a2023-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 Glass5e2607a2023-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
35 INIT_LIST_HEAD(&scn->obj_head);
36 scn->id = resolve_id(exp, id);
37 scn->expo = exp;
38 list_add_tail(&scn->sibling, &exp->scene_head);
39
40 *scnp = scn;
41
42 return scn->id;
43}
44
45void scene_obj_destroy(struct scene_obj *obj)
46{
47 if (obj->type == SCENEOBJT_MENU)
48 scene_menu_destroy((struct scene_obj_menu *)obj);
49 free(obj->name);
50 free(obj);
51}
52
53void scene_destroy(struct scene *scn)
54{
55 struct scene_obj *obj, *next;
56
57 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
58 scene_obj_destroy(obj);
59
60 free(scn->name);
Simon Glass5e2607a2023-01-06 08:52:37 -060061 free(scn);
62}
63
Simon Glassdef898c2023-06-01 10:22:27 -060064int scene_title_set(struct scene *scn, uint id)
Simon Glass5e2607a2023-01-06 08:52:37 -060065{
Simon Glassdef898c2023-06-01 10:22:27 -060066 scn->title_id = id;
Simon Glass5e2607a2023-01-06 08:52:37 -060067
68 return 0;
69}
70
71int scene_obj_count(struct scene *scn)
72{
73 struct scene_obj *obj;
74 int count = 0;
75
76 list_for_each_entry(obj, &scn->obj_head, sibling)
77 count++;
78
79 return count;
80}
81
Simon Glass633b3dc2023-08-14 16:40:21 -060082void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
Simon Glass5e2607a2023-01-06 08:52:37 -060083{
84 struct scene_obj *obj;
85
86 list_for_each_entry(obj, &scn->obj_head, sibling) {
87 if (obj->id == id &&
88 (type == SCENEOBJT_NONE || obj->type == type))
89 return obj;
90 }
91
92 return NULL;
93}
94
Simon Glassa0874dc2023-06-01 10:23:02 -060095void *scene_obj_find_by_name(struct scene *scn, const char *name)
96{
97 struct scene_obj *obj;
98
99 list_for_each_entry(obj, &scn->obj_head, sibling) {
100 if (!strcmp(name, obj->name))
101 return obj;
102 }
103
104 return NULL;
105}
106
Simon Glass5e2607a2023-01-06 08:52:37 -0600107int scene_obj_add(struct scene *scn, const char *name, uint id,
108 enum scene_obj_t type, uint size, struct scene_obj **objp)
109{
110 struct scene_obj *obj;
111
112 obj = calloc(1, size);
113 if (!obj)
114 return log_msg_ret("obj", -ENOMEM);
115 obj->name = strdup(name);
116 if (!obj->name) {
117 free(obj);
118 return log_msg_ret("name", -ENOMEM);
119 }
120
121 obj->id = resolve_id(scn->expo, id);
122 obj->scene = scn;
123 obj->type = type;
124 list_add_tail(&obj->sibling, &scn->obj_head);
125 *objp = obj;
126
127 return obj->id;
128}
129
130int scene_img(struct scene *scn, const char *name, uint id, char *data,
131 struct scene_obj_img **imgp)
132{
133 struct scene_obj_img *img;
134 int ret;
135
136 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
137 sizeof(struct scene_obj_img),
138 (struct scene_obj **)&img);
139 if (ret < 0)
140 return log_msg_ret("obj", -ENOMEM);
141
142 img->data = data;
143
144 if (imgp)
145 *imgp = img;
146
147 return img->obj.id;
148}
149
150int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
151 struct scene_obj_txt **txtp)
152{
153 struct scene_obj_txt *txt;
154 int ret;
155
156 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
157 sizeof(struct scene_obj_txt),
158 (struct scene_obj **)&txt);
159 if (ret < 0)
160 return log_msg_ret("obj", -ENOMEM);
161
162 txt->str_id = str_id;
163
164 if (txtp)
165 *txtp = txt;
166
167 return txt->obj.id;
168}
169
170int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
171 const char *str, struct scene_obj_txt **txtp)
172{
173 struct scene_obj_txt *txt;
174 int ret;
175
176 ret = expo_str(scn->expo, name, str_id, str);
177 if (ret < 0)
178 return log_msg_ret("str", ret);
179 else if (ret != str_id)
180 return log_msg_ret("id", -EEXIST);
181
182 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
183 sizeof(struct scene_obj_txt),
184 (struct scene_obj **)&txt);
185 if (ret < 0)
186 return log_msg_ret("obj", -ENOMEM);
187
188 txt->str_id = str_id;
189
190 if (txtp)
191 *txtp = txt;
192
193 return txt->obj.id;
194}
195
196int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
197 uint font_size)
198{
199 struct scene_obj_txt *txt;
200
201 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
202 if (!txt)
203 return log_msg_ret("find", -ENOENT);
204 txt->font_name = font_name;
205 txt->font_size = font_size;
206
207 return 0;
208}
209
210int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
211{
212 struct scene_obj *obj;
213
214 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
215 if (!obj)
216 return log_msg_ret("find", -ENOENT);
Simon Glassae45d6c2023-06-01 10:22:49 -0600217 obj->dim.x = x;
218 obj->dim.y = y;
Simon Glass5e2607a2023-01-06 08:52:37 -0600219
220 return 0;
221}
222
Simon Glass699b0ac2023-06-01 10:22:52 -0600223int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
224{
225 struct scene_obj *obj;
226
227 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
228 if (!obj)
229 return log_msg_ret("find", -ENOENT);
230 obj->dim.w = w;
231 obj->dim.h = h;
232
233 return 0;
234}
235
Simon Glass5e2607a2023-01-06 08:52:37 -0600236int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
237{
Simon Glassce72c9e2023-06-01 10:22:50 -0600238 int ret;
239
240 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
241 hide ? SCENEOF_HIDE : 0);
242 if (ret)
243 return log_msg_ret("flg", ret);
244
245 return 0;
246}
247
248int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
249{
Simon Glass5e2607a2023-01-06 08:52:37 -0600250 struct scene_obj *obj;
251
252 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
253 if (!obj)
254 return log_msg_ret("find", -ENOENT);
Simon Glassce72c9e2023-06-01 10:22:50 -0600255 obj->flags &= ~clr;
256 obj->flags |= set;
Simon Glass5e2607a2023-01-06 08:52:37 -0600257
258 return 0;
259}
260
261int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
262{
263 struct scene_obj *obj;
264
265 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
266 if (!obj)
267 return log_msg_ret("find", -ENOENT);
268
269 switch (obj->type) {
270 case SCENEOBJT_NONE:
271 case SCENEOBJT_MENU:
272 break;
273 case SCENEOBJT_IMAGE: {
274 struct scene_obj_img *img = (struct scene_obj_img *)obj;
275 ulong width, height;
276 uint bpix;
277
278 video_bmp_get_info(img->data, &width, &height, &bpix);
279 if (widthp)
280 *widthp = width;
281 return height;
282 }
283 case SCENEOBJT_TEXT: {
284 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
285 struct expo *exp = scn->expo;
Simon Glass50f02032023-06-01 10:22:51 -0600286 struct vidconsole_bbox bbox;
287 const char *str;
288 int len, ret;
Simon Glass5e2607a2023-01-06 08:52:37 -0600289
Simon Glass50f02032023-06-01 10:22:51 -0600290 str = expo_get_str(exp, txt->str_id);
291 if (!str)
292 return log_msg_ret("str", -ENOENT);
293 len = strlen(str);
294
295 /* if there is no console, make it up */
296 if (!exp->cons) {
297 if (widthp)
298 *widthp = 8 * len;
299 return 16;
300 }
301
302 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
303 txt->font_size, str, &bbox);
304 if (ret)
305 return log_msg_ret("mea", ret);
Simon Glass5e2607a2023-01-06 08:52:37 -0600306 if (widthp)
Simon Glass50f02032023-06-01 10:22:51 -0600307 *widthp = bbox.x1;
Simon Glass5e2607a2023-01-06 08:52:37 -0600308
Simon Glass50f02032023-06-01 10:22:51 -0600309 return bbox.y1;
Simon Glass5e2607a2023-01-06 08:52:37 -0600310 }
311 }
312
313 return 0;
314}
315
316/**
317 * scene_obj_render() - Render an object
318 *
319 */
320static int scene_obj_render(struct scene_obj *obj, bool text_mode)
321{
322 struct scene *scn = obj->scene;
323 struct expo *exp = scn->expo;
Simon Glass7230fdb2023-06-01 10:23:00 -0600324 const struct expo_theme *theme = &exp->theme;
Simon Glass42b18492023-06-01 10:22:34 -0600325 struct udevice *dev = exp->display;
326 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass5e2607a2023-01-06 08:52:37 -0600327 int x, y, ret;
328
Simon Glassae45d6c2023-06-01 10:22:49 -0600329 x = obj->dim.x;
330 y = obj->dim.y;
Simon Glass5e2607a2023-01-06 08:52:37 -0600331
332 switch (obj->type) {
333 case SCENEOBJT_NONE:
334 break;
335 case SCENEOBJT_IMAGE: {
336 struct scene_obj_img *img = (struct scene_obj_img *)obj;
337
338 if (!cons)
339 return -ENOTSUPP;
340 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
341 true);
342 if (ret < 0)
343 return log_msg_ret("img", ret);
344 break;
345 }
346 case SCENEOBJT_TEXT: {
347 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
348 const char *str;
349
350 if (!cons)
351 return -ENOTSUPP;
352
353 if (txt->font_name || txt->font_size) {
354 ret = vidconsole_select_font(cons,
355 txt->font_name,
356 txt->font_size);
357 } else {
358 ret = vidconsole_select_font(cons, NULL, 0);
359 }
360 if (ret && ret != -ENOSYS)
361 return log_msg_ret("font", ret);
Simon Glass5e2607a2023-01-06 08:52:37 -0600362 str = expo_get_str(exp, txt->str_id);
Simon Glass756c9552023-06-01 10:22:57 -0600363 if (str) {
364 struct video_priv *vid_priv;
365 struct vidconsole_colour old;
366 enum colour_idx fore, back;
367
368 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
369 fore = VID_BLACK;
370 back = VID_WHITE;
371 } else {
372 fore = VID_LIGHT_GRAY;
373 back = VID_BLACK;
374 }
375
376 vid_priv = dev_get_uclass_priv(dev);
377 if (obj->flags & SCENEOF_POINT) {
378 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass7230fdb2023-06-01 10:23:00 -0600379 video_fill_part(dev, x - theme->menu_inset, y,
380 x + obj->dim.w,
381 y + obj->dim.h,
Simon Glass756c9552023-06-01 10:22:57 -0600382 vid_priv->colour_bg);
383 }
384 vidconsole_set_cursor_pos(cons, x, y);
Simon Glass5e2607a2023-01-06 08:52:37 -0600385 vidconsole_put_string(cons, str);
Simon Glass756c9552023-06-01 10:22:57 -0600386 if (obj->flags & SCENEOF_POINT)
387 vidconsole_pop_colour(cons, &old);
388 }
Simon Glass5e2607a2023-01-06 08:52:37 -0600389 break;
390 }
391 case SCENEOBJT_MENU: {
392 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass756c9552023-06-01 10:22:57 -0600393
394 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
395 if (!cons)
396 return -ENOTSUPP;
397
398 /* draw a background behind the menu items */
399 scene_menu_render(menu);
400 }
Simon Glass5e2607a2023-01-06 08:52:37 -0600401 /*
402 * With a vidconsole, the text and item pointer are rendered as
403 * normal objects so we don't need to do anything here. The menu
404 * simply controls where they are positioned.
405 */
406 if (cons)
407 return -ENOTSUPP;
408
409 ret = scene_menu_display(menu);
410 if (ret < 0)
411 return log_msg_ret("img", ret);
412
413 break;
414 }
415 }
416
417 return 0;
418}
419
420int scene_arrange(struct scene *scn)
421{
422 struct scene_obj *obj;
423 int ret;
424
425 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassfd6073a2023-10-01 19:13:24 -0600426 switch (obj->type) {
427 case SCENEOBJT_NONE:
428 case SCENEOBJT_IMAGE:
429 case SCENEOBJT_TEXT:
430 break;
431 case SCENEOBJT_MENU: {
Simon Glass5e2607a2023-01-06 08:52:37 -0600432 struct scene_obj_menu *menu;
433
434 menu = (struct scene_obj_menu *)obj,
435 ret = scene_menu_arrange(scn, menu);
436 if (ret)
437 return log_msg_ret("arr", ret);
Simon Glassfd6073a2023-10-01 19:13:24 -0600438 break;
439 }
Simon Glass5e2607a2023-01-06 08:52:37 -0600440 }
441 }
442
443 return 0;
444}
445
Simon Glass4c87e072023-06-01 10:22:58 -0600446int scene_render_deps(struct scene *scn, uint id)
447{
448 struct scene_obj *obj;
449 int ret;
450
451 if (!id)
452 return 0;
453 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
454 if (!obj)
455 return log_msg_ret("obj", -ENOENT);
456
457 if (!(obj->flags & SCENEOF_HIDE)) {
458 ret = scene_obj_render(obj, false);
459 if (ret && ret != -ENOTSUPP)
460 return log_msg_ret("ren", ret);
461
Simon Glassfd6073a2023-10-01 19:13:24 -0600462 switch (obj->type) {
463 case SCENEOBJT_NONE:
464 case SCENEOBJT_IMAGE:
465 case SCENEOBJT_TEXT:
466 break;
467 case SCENEOBJT_MENU:
Simon Glass4c87e072023-06-01 10:22:58 -0600468 scene_menu_render_deps(scn,
469 (struct scene_obj_menu *)obj);
Simon Glassfd6073a2023-10-01 19:13:24 -0600470 break;
471 }
Simon Glass4c87e072023-06-01 10:22:58 -0600472 }
473
474 return 0;
475}
476
Simon Glass5e2607a2023-01-06 08:52:37 -0600477int scene_render(struct scene *scn)
478{
479 struct expo *exp = scn->expo;
480 struct scene_obj *obj;
481 int ret;
482
483 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassce72c9e2023-06-01 10:22:50 -0600484 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass5e2607a2023-01-06 08:52:37 -0600485 ret = scene_obj_render(obj, exp->text_mode);
486 if (ret && ret != -ENOTSUPP)
487 return log_msg_ret("ren", ret);
488 }
489 }
490
Simon Glass4c87e072023-06-01 10:22:58 -0600491 /* render any highlighted object on top of the others */
492 if (scn->highlight_id && !exp->text_mode) {
493 ret = scene_render_deps(scn, scn->highlight_id);
494 if (ret && ret != -ENOTSUPP)
495 return log_msg_ret("dep", ret);
496 }
497
Simon Glass5e2607a2023-01-06 08:52:37 -0600498 return 0;
499}
500
Simon Glass4e64bee2023-06-01 10:22:59 -0600501/**
502 * send_key_obj() - Handle a keypress for moving between objects
503 *
504 * @scn: Scene to receive the key
505 * @key: Key to send (KEYCODE_UP)
506 * @event: Returns resulting event from this keypress
507 * Returns: 0 if OK, -ve on error
508 */
509static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
510 struct expo_action *event)
511{
512 switch (key) {
513 case BKEY_UP:
514 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
515 sibling)) {
516 obj = list_entry(obj->sibling.prev,
517 struct scene_obj, sibling);
518 if (obj->type == SCENEOBJT_MENU) {
519 event->type = EXPOACT_POINT_OBJ;
520 event->select.id = obj->id;
521 log_debug("up to obj %d\n", event->select.id);
522 break;
523 }
524 }
525 break;
526 case BKEY_DOWN:
527 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
528 obj = list_entry(obj->sibling.next, struct scene_obj,
529 sibling);
530 if (obj->type == SCENEOBJT_MENU) {
531 event->type = EXPOACT_POINT_OBJ;
532 event->select.id = obj->id;
533 log_debug("down to obj %d\n", event->select.id);
534 break;
535 }
536 }
537 break;
538 case BKEY_SELECT:
539 if (obj->type == SCENEOBJT_MENU) {
540 event->type = EXPOACT_OPEN;
541 event->select.id = obj->id;
542 log_debug("open obj %d\n", event->select.id);
543 }
544 break;
545 case BKEY_QUIT:
546 event->type = EXPOACT_QUIT;
547 log_debug("obj quit\n");
548 break;
549 }
550}
551
Simon Glass5e2607a2023-01-06 08:52:37 -0600552int scene_send_key(struct scene *scn, int key, struct expo_action *event)
553{
554 struct scene_obj *obj;
555 int ret;
556
Simon Glass4e64bee2023-06-01 10:22:59 -0600557 event->type = EXPOACT_NONE;
558
559 /*
560 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
561 * opened
562 */
563 if (scn->expo->popup) {
564 obj = NULL;
565 if (scn->highlight_id) {
566 obj = scene_obj_find(scn, scn->highlight_id,
567 SCENEOBJT_NONE);
568 }
569 if (!obj)
570 return 0;
571
572 if (!(obj->flags & SCENEOF_OPEN)) {
573 send_key_obj(scn, obj, key, event);
574 return 0;
575 }
576
Simon Glassfd6073a2023-10-01 19:13:24 -0600577 switch (obj->type) {
578 case SCENEOBJT_NONE:
579 case SCENEOBJT_IMAGE:
580 case SCENEOBJT_TEXT:
581 break;
582 case SCENEOBJT_MENU: {
583 struct scene_obj_menu *menu;
584
585 menu = (struct scene_obj_menu *)obj,
586 ret = scene_menu_send_key(scn, menu, key, event);
587 if (ret)
588 return log_msg_ret("key", ret);
589 break;
590 }
591 }
Simon Glass4e64bee2023-06-01 10:22:59 -0600592 return 0;
593 }
594
Simon Glass5e2607a2023-01-06 08:52:37 -0600595 list_for_each_entry(obj, &scn->obj_head, sibling) {
596 if (obj->type == SCENEOBJT_MENU) {
597 struct scene_obj_menu *menu;
598
599 menu = (struct scene_obj_menu *)obj,
600 ret = scene_menu_send_key(scn, menu, key, event);
601 if (ret)
602 return log_msg_ret("key", ret);
Simon Glass5e2607a2023-01-06 08:52:37 -0600603 break;
604 }
605 }
606
607 return 0;
608}
Simon Glass699b0ac2023-06-01 10:22:52 -0600609
610int scene_calc_dims(struct scene *scn, bool do_menus)
611{
612 struct scene_obj *obj;
613 int ret;
614
615 list_for_each_entry(obj, &scn->obj_head, sibling) {
616 switch (obj->type) {
617 case SCENEOBJT_NONE:
618 case SCENEOBJT_TEXT:
619 case SCENEOBJT_IMAGE: {
620 int width;
621
622 if (!do_menus) {
623 ret = scene_obj_get_hw(scn, obj->id, &width);
624 if (ret < 0)
625 return log_msg_ret("get", ret);
626 obj->dim.w = width;
627 obj->dim.h = ret;
628 }
629 break;
630 }
631 case SCENEOBJT_MENU: {
632 struct scene_obj_menu *menu;
633
634 if (do_menus) {
635 menu = (struct scene_obj_menu *)obj;
636
637 ret = scene_menu_calc_dims(menu);
638 if (ret)
639 return log_msg_ret("men", ret);
640 }
641 break;
642 }
643 }
644 }
645
646 return 0;
647}
Simon Glass2e593892023-06-01 10:22:53 -0600648
649int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
650{
651 struct scene_obj *obj;
652 int ret;
653
654 /* Avoid error-checking optional items */
655 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
656
657 list_for_each_entry(obj, &scn->obj_head, sibling) {
658 switch (obj->type) {
659 case SCENEOBJT_NONE:
660 case SCENEOBJT_IMAGE:
661 case SCENEOBJT_MENU:
662 break;
663 case SCENEOBJT_TEXT:
664 scene_txt_set_font(scn, obj->id, NULL,
665 theme->font_size);
666 break;
667 }
668 }
669
670 ret = scene_arrange(scn);
671 if (ret)
672 return log_msg_ret("arr", ret);
673
674 return 0;
675}
Simon Glass756c9552023-06-01 10:22:57 -0600676
677void scene_set_highlight_id(struct scene *scn, uint id)
678{
679 scn->highlight_id = id;
680}
681
682void scene_highlight_first(struct scene *scn)
683{
684 struct scene_obj *obj;
685
686 list_for_each_entry(obj, &scn->obj_head, sibling) {
687 switch (obj->type) {
688 case SCENEOBJT_MENU:
689 scene_set_highlight_id(scn, obj->id);
690 return;
691 default:
692 break;
693 }
694 }
695}
696
697int scene_set_open(struct scene *scn, uint id, bool open)
698{
699 int ret;
700
701 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
702 open ? SCENEOF_OPEN : 0);
703 if (ret)
704 return log_msg_ret("flg", ret);
705
706 return 0;
707}
Simon Glassf2eb6ad2023-08-14 16:40:23 -0600708
709int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
710 void *priv)
711{
712 struct scene_obj *obj;
713
714 list_for_each_entry(obj, &scn->obj_head, sibling) {
715 int ret;
716
717 ret = iter(obj, priv);
718 if (ret)
719 return log_msg_ret("itr", ret);
720 }
721
722 return 0;
723}