doc: fix mistyped "env flags" command
[pandora-u-boot.git] / boot / scene.c
1 // 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
9 #define LOG_CATEGORY    LOGC_EXPO
10
11 #include <common.h>
12 #include <dm.h>
13 #include <expo.h>
14 #include <malloc.h>
15 #include <mapmem.h>
16 #include <menu.h>
17 #include <video.h>
18 #include <video_console.h>
19 #include <linux/input.h>
20 #include "scene_internal.h"
21
22 int 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         abuf_init(&scn->buf);
36         if (!abuf_realloc(&scn->buf, EXPO_MAX_CHARS + 1)) {
37                 free(scn->name);
38                 free(scn);
39                 return log_msg_ret("buf", -ENOMEM);
40         }
41         abuf_init(&scn->entry_save);
42
43         INIT_LIST_HEAD(&scn->obj_head);
44         scn->id = resolve_id(exp, id);
45         scn->expo = exp;
46         list_add_tail(&scn->sibling, &exp->scene_head);
47
48         *scnp = scn;
49
50         return scn->id;
51 }
52
53 void scene_obj_destroy(struct scene_obj *obj)
54 {
55         if (obj->type == SCENEOBJT_MENU)
56                 scene_menu_destroy((struct scene_obj_menu *)obj);
57         free(obj->name);
58         free(obj);
59 }
60
61 void scene_destroy(struct scene *scn)
62 {
63         struct scene_obj *obj, *next;
64
65         list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
66                 scene_obj_destroy(obj);
67
68         abuf_uninit(&scn->entry_save);
69         abuf_uninit(&scn->buf);
70         free(scn->name);
71         free(scn);
72 }
73
74 int scene_title_set(struct scene *scn, uint id)
75 {
76         scn->title_id = id;
77
78         return 0;
79 }
80
81 int scene_obj_count(struct scene *scn)
82 {
83         struct scene_obj *obj;
84         int count = 0;
85
86         list_for_each_entry(obj, &scn->obj_head, sibling)
87                 count++;
88
89         return count;
90 }
91
92 void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
93 {
94         struct scene_obj *obj;
95
96         list_for_each_entry(obj, &scn->obj_head, sibling) {
97                 if (obj->id == id &&
98                     (type == SCENEOBJT_NONE || obj->type == type))
99                         return obj;
100         }
101
102         return NULL;
103 }
104
105 void *scene_obj_find_by_name(struct scene *scn, const char *name)
106 {
107         struct scene_obj *obj;
108
109         list_for_each_entry(obj, &scn->obj_head, sibling) {
110                 if (!strcmp(name, obj->name))
111                         return obj;
112         }
113
114         return NULL;
115 }
116
117 int scene_obj_add(struct scene *scn, const char *name, uint id,
118                   enum scene_obj_t type, uint size, struct scene_obj **objp)
119 {
120         struct scene_obj *obj;
121
122         obj = calloc(1, size);
123         if (!obj)
124                 return log_msg_ret("obj", -ENOMEM);
125         obj->name = strdup(name);
126         if (!obj->name) {
127                 free(obj);
128                 return log_msg_ret("name", -ENOMEM);
129         }
130
131         obj->id = resolve_id(scn->expo, id);
132         obj->scene = scn;
133         obj->type = type;
134         list_add_tail(&obj->sibling, &scn->obj_head);
135         *objp = obj;
136
137         return obj->id;
138 }
139
140 int scene_img(struct scene *scn, const char *name, uint id, char *data,
141               struct scene_obj_img **imgp)
142 {
143         struct scene_obj_img *img;
144         int ret;
145
146         ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
147                             sizeof(struct scene_obj_img),
148                             (struct scene_obj **)&img);
149         if (ret < 0)
150                 return log_msg_ret("obj", ret);
151
152         img->data = data;
153
154         if (imgp)
155                 *imgp = img;
156
157         return img->obj.id;
158 }
159
160 int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
161               struct scene_obj_txt **txtp)
162 {
163         struct scene_obj_txt *txt;
164         int ret;
165
166         ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
167                             sizeof(struct scene_obj_txt),
168                             (struct scene_obj **)&txt);
169         if (ret < 0)
170                 return log_msg_ret("obj", ret);
171
172         txt->str_id = str_id;
173
174         if (txtp)
175                 *txtp = txt;
176
177         return txt->obj.id;
178 }
179
180 int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
181                   const char *str, struct scene_obj_txt **txtp)
182 {
183         struct scene_obj_txt *txt;
184         int ret;
185
186         ret = expo_str(scn->expo, name, str_id, str);
187         if (ret < 0)
188                 return log_msg_ret("str", ret);
189         if (str_id && ret != str_id)
190                 return log_msg_ret("id", -EEXIST);
191         str_id = ret;
192
193         ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
194                             sizeof(struct scene_obj_txt),
195                             (struct scene_obj **)&txt);
196         if (ret < 0)
197                 return log_msg_ret("obj", ret);
198
199         txt->str_id = str_id;
200
201         if (txtp)
202                 *txtp = txt;
203
204         return txt->obj.id;
205 }
206
207 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
208                        uint font_size)
209 {
210         struct scene_obj_txt *txt;
211
212         txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
213         if (!txt)
214                 return log_msg_ret("find", -ENOENT);
215         txt->font_name = font_name;
216         txt->font_size = font_size;
217
218         return 0;
219 }
220
221 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
222 {
223         struct scene_obj *obj;
224
225         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
226         if (!obj)
227                 return log_msg_ret("find", -ENOENT);
228         obj->dim.x = x;
229         obj->dim.y = y;
230
231         return 0;
232 }
233
234 int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
235 {
236         struct scene_obj *obj;
237
238         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
239         if (!obj)
240                 return log_msg_ret("find", -ENOENT);
241         obj->dim.w = w;
242         obj->dim.h = h;
243
244         return 0;
245 }
246
247 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
248 {
249         int ret;
250
251         ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
252                                     hide ? SCENEOF_HIDE : 0);
253         if (ret)
254                 return log_msg_ret("flg", ret);
255
256         return 0;
257 }
258
259 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
260 {
261         struct scene_obj *obj;
262
263         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
264         if (!obj)
265                 return log_msg_ret("find", -ENOENT);
266         obj->flags &= ~clr;
267         obj->flags |= set;
268
269         return 0;
270 }
271
272 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
273 {
274         struct scene_obj *obj;
275
276         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
277         if (!obj)
278                 return log_msg_ret("find", -ENOENT);
279
280         switch (obj->type) {
281         case SCENEOBJT_NONE:
282         case SCENEOBJT_MENU:
283         case SCENEOBJT_TEXTLINE:
284                 break;
285         case SCENEOBJT_IMAGE: {
286                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
287                 ulong width, height;
288                 uint bpix;
289
290                 video_bmp_get_info(img->data, &width, &height, &bpix);
291                 if (widthp)
292                         *widthp = width;
293                 return height;
294         }
295         case SCENEOBJT_TEXT: {
296                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
297                 struct expo *exp = scn->expo;
298                 struct vidconsole_bbox bbox;
299                 const char *str;
300                 int len, ret;
301
302                 str = expo_get_str(exp, txt->str_id);
303                 if (!str)
304                         return log_msg_ret("str", -ENOENT);
305                 len = strlen(str);
306
307                 /* if there is no console, make it up */
308                 if (!exp->cons) {
309                         if (widthp)
310                                 *widthp = 8 * len;
311                         return 16;
312                 }
313
314                 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
315                                          txt->font_size, str, &bbox);
316                 if (ret)
317                         return log_msg_ret("mea", ret);
318                 if (widthp)
319                         *widthp = bbox.x1;
320
321                 return bbox.y1;
322         }
323         }
324
325         return 0;
326 }
327
328 /**
329  * scene_render_background() - Render the background for an object
330  *
331  * @obj: Object to render
332  * @box_only: true to show a box around the object, but keep the normal
333  * background colour inside
334  */
335 static void scene_render_background(struct scene_obj *obj, bool box_only)
336 {
337         struct expo *exp = obj->scene->expo;
338         const struct expo_theme *theme = &exp->theme;
339         struct vidconsole_bbox bbox, label_bbox;
340         struct udevice *dev = exp->display;
341         struct video_priv *vid_priv;
342         struct udevice *cons = exp->cons;
343         struct vidconsole_colour old;
344         enum colour_idx fore, back;
345         uint inset = theme->menu_inset;
346
347         /* draw a background for the object */
348         if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
349                 fore = VID_BLACK;
350                 back = VID_WHITE;
351         } else {
352                 fore = VID_LIGHT_GRAY;
353                 back = VID_BLACK;
354         }
355
356         /* see if this object wants to render a background */
357         if (scene_obj_calc_bbox(obj, &bbox, &label_bbox))
358                 return;
359
360         vidconsole_push_colour(cons, fore, back, &old);
361         vid_priv = dev_get_uclass_priv(dev);
362         video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
363                         label_bbox.x1 + inset, label_bbox.y1 + inset,
364                         vid_priv->colour_fg);
365         vidconsole_pop_colour(cons, &old);
366         if (box_only) {
367                 video_fill_part(dev, label_bbox.x0, label_bbox.y0,
368                                 label_bbox.x1, label_bbox.y1,
369                                 vid_priv->colour_bg);
370         }
371 }
372
373 /**
374  * scene_obj_render() - Render an object
375  *
376  */
377 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
378 {
379         struct scene *scn = obj->scene;
380         struct expo *exp = scn->expo;
381         const struct expo_theme *theme = &exp->theme;
382         struct udevice *dev = exp->display;
383         struct udevice *cons = text_mode ? NULL : exp->cons;
384         int x, y, ret;
385
386         x = obj->dim.x;
387         y = obj->dim.y;
388
389         switch (obj->type) {
390         case SCENEOBJT_NONE:
391                 break;
392         case SCENEOBJT_IMAGE: {
393                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
394
395                 if (!cons)
396                         return -ENOTSUPP;
397                 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
398                                         true);
399                 if (ret < 0)
400                         return log_msg_ret("img", ret);
401                 break;
402         }
403         case SCENEOBJT_TEXT: {
404                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
405                 const char *str;
406
407                 if (!cons)
408                         return -ENOTSUPP;
409
410                 if (txt->font_name || txt->font_size) {
411                         ret = vidconsole_select_font(cons,
412                                                      txt->font_name,
413                                                      txt->font_size);
414                 } else {
415                         ret = vidconsole_select_font(cons, NULL, 0);
416                 }
417                 if (ret && ret != -ENOSYS)
418                         return log_msg_ret("font", ret);
419                 str = expo_get_str(exp, txt->str_id);
420                 if (str) {
421                         struct video_priv *vid_priv;
422                         struct vidconsole_colour old;
423                         enum colour_idx fore, back;
424
425                         if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
426                                 fore = VID_BLACK;
427                                 back = VID_WHITE;
428                         } else {
429                                 fore = VID_LIGHT_GRAY;
430                                 back = VID_BLACK;
431                         }
432
433                         vid_priv = dev_get_uclass_priv(dev);
434                         if (obj->flags & SCENEOF_POINT) {
435                                 vidconsole_push_colour(cons, fore, back, &old);
436                                 video_fill_part(dev, x - theme->menu_inset, y,
437                                                 x + obj->dim.w,
438                                                 y + obj->dim.h,
439                                                 vid_priv->colour_bg);
440                         }
441                         vidconsole_set_cursor_pos(cons, x, y);
442                         vidconsole_put_string(cons, str);
443                         if (obj->flags & SCENEOF_POINT)
444                                 vidconsole_pop_colour(cons, &old);
445                 }
446                 break;
447         }
448         case SCENEOBJT_MENU: {
449                 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
450
451                 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
452                         if (!cons)
453                                 return -ENOTSUPP;
454
455                         /* draw a background behind the menu items */
456                         scene_render_background(obj, false);
457                 }
458                 /*
459                  * With a vidconsole, the text and item pointer are rendered as
460                  * normal objects so we don't need to do anything here. The menu
461                  * simply controls where they are positioned.
462                  */
463                 if (cons)
464                         return -ENOTSUPP;
465
466                 ret = scene_menu_display(menu);
467                 if (ret < 0)
468                         return log_msg_ret("img", ret);
469
470                 break;
471         }
472         case SCENEOBJT_TEXTLINE:
473                 if (obj->flags & SCENEOF_OPEN)
474                         scene_render_background(obj, true);
475                 break;
476         }
477
478         return 0;
479 }
480
481 int scene_arrange(struct scene *scn)
482 {
483         struct scene_obj *obj;
484         int ret;
485
486         list_for_each_entry(obj, &scn->obj_head, sibling) {
487                 switch (obj->type) {
488                 case SCENEOBJT_NONE:
489                 case SCENEOBJT_IMAGE:
490                 case SCENEOBJT_TEXT:
491                         break;
492                 case SCENEOBJT_MENU: {
493                         struct scene_obj_menu *menu;
494
495                         menu = (struct scene_obj_menu *)obj,
496                         ret = scene_menu_arrange(scn, menu);
497                         if (ret)
498                                 return log_msg_ret("arr", ret);
499                         break;
500                 }
501                 case SCENEOBJT_TEXTLINE: {
502                         struct scene_obj_textline *tline;
503
504                         tline = (struct scene_obj_textline *)obj,
505                         ret = scene_textline_arrange(scn, tline);
506                         if (ret)
507                                 return log_msg_ret("arr", ret);
508                         break;
509                 }
510                 }
511         }
512
513         return 0;
514 }
515
516 int scene_render_deps(struct scene *scn, uint id)
517 {
518         struct scene_obj *obj;
519         int ret;
520
521         if (!id)
522                 return 0;
523         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
524         if (!obj)
525                 return log_msg_ret("obj", -ENOENT);
526
527         if (!(obj->flags & SCENEOF_HIDE)) {
528                 ret = scene_obj_render(obj, false);
529                 if (ret && ret != -ENOTSUPP)
530                         return log_msg_ret("ren", ret);
531
532                 switch (obj->type) {
533                 case SCENEOBJT_NONE:
534                 case SCENEOBJT_IMAGE:
535                 case SCENEOBJT_TEXT:
536                         break;
537                 case SCENEOBJT_MENU:
538                         scene_menu_render_deps(scn,
539                                                (struct scene_obj_menu *)obj);
540                         break;
541                 case SCENEOBJT_TEXTLINE:
542                         scene_textline_render_deps(scn,
543                                         (struct scene_obj_textline *)obj);
544                         break;
545                 }
546         }
547
548         return 0;
549 }
550
551 int scene_render(struct scene *scn)
552 {
553         struct expo *exp = scn->expo;
554         struct scene_obj *obj;
555         int ret;
556
557         list_for_each_entry(obj, &scn->obj_head, sibling) {
558                 if (!(obj->flags & SCENEOF_HIDE)) {
559                         ret = scene_obj_render(obj, exp->text_mode);
560                         if (ret && ret != -ENOTSUPP)
561                                 return log_msg_ret("ren", ret);
562                 }
563         }
564
565         /* render any highlighted object on top of the others */
566         if (scn->highlight_id && !exp->text_mode) {
567                 ret = scene_render_deps(scn, scn->highlight_id);
568                 if (ret && ret != -ENOTSUPP)
569                         return log_msg_ret("dep", ret);
570         }
571
572         return 0;
573 }
574
575 /**
576  * send_key_obj() - Handle a keypress for moving between objects
577  *
578  * @scn: Scene to receive the key
579  * @key: Key to send (KEYCODE_UP)
580  * @event: Returns resulting event from this keypress
581  * Returns: 0 if OK, -ve on error
582  */
583 static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
584                          struct expo_action *event)
585 {
586         switch (key) {
587         case BKEY_UP:
588                 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
589                                                sibling)) {
590                         obj = list_entry(obj->sibling.prev,
591                                          struct scene_obj, sibling);
592                         if (scene_obj_can_highlight(obj)) {
593                                 event->type = EXPOACT_POINT_OBJ;
594                                 event->select.id = obj->id;
595                                 log_debug("up to obj %d\n", event->select.id);
596                                 break;
597                         }
598                 }
599                 break;
600         case BKEY_DOWN:
601                 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
602                         obj = list_entry(obj->sibling.next, struct scene_obj,
603                                          sibling);
604                         if (scene_obj_can_highlight(obj)) {
605                                 event->type = EXPOACT_POINT_OBJ;
606                                 event->select.id = obj->id;
607                                 log_debug("down to obj %d\n", event->select.id);
608                                 break;
609                         }
610                 }
611                 break;
612         case BKEY_SELECT:
613                 if (scene_obj_can_highlight(obj)) {
614                         event->type = EXPOACT_OPEN;
615                         event->select.id = obj->id;
616                         log_debug("open obj %d\n", event->select.id);
617                 }
618                 break;
619         case BKEY_QUIT:
620                 event->type = EXPOACT_QUIT;
621                 log_debug("obj quit\n");
622                 break;
623         }
624 }
625
626 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
627 {
628         struct scene_obj *obj;
629         int ret;
630
631         event->type = EXPOACT_NONE;
632
633         /*
634          * In 'popup' mode, arrow keys move betwen objects, unless a menu is
635          * opened
636          */
637         if (scn->expo->popup) {
638                 obj = NULL;
639                 if (scn->highlight_id) {
640                         obj = scene_obj_find(scn, scn->highlight_id,
641                                              SCENEOBJT_NONE);
642                 }
643                 if (!obj)
644                         return 0;
645
646                 if (!(obj->flags & SCENEOF_OPEN)) {
647                         send_key_obj(scn, obj, key, event);
648                         return 0;
649                 }
650
651                 switch (obj->type) {
652                 case SCENEOBJT_NONE:
653                 case SCENEOBJT_IMAGE:
654                 case SCENEOBJT_TEXT:
655                         break;
656                 case SCENEOBJT_MENU: {
657                         struct scene_obj_menu *menu;
658
659                         menu = (struct scene_obj_menu *)obj,
660                         ret = scene_menu_send_key(scn, menu, key, event);
661                         if (ret)
662                                 return log_msg_ret("key", ret);
663                         break;
664                 }
665                 case SCENEOBJT_TEXTLINE: {
666                         struct scene_obj_textline *tline;
667
668                         tline = (struct scene_obj_textline *)obj,
669                         ret = scene_textline_send_key(scn, tline, key, event);
670                         if (ret)
671                                 return log_msg_ret("key", ret);
672                         break;
673                 }
674                 }
675                 return 0;
676         }
677
678         list_for_each_entry(obj, &scn->obj_head, sibling) {
679                 if (obj->type == SCENEOBJT_MENU) {
680                         struct scene_obj_menu *menu;
681
682                         menu = (struct scene_obj_menu *)obj,
683                         ret = scene_menu_send_key(scn, menu, key, event);
684                         if (ret)
685                                 return log_msg_ret("key", ret);
686                         break;
687                 }
688         }
689
690         return 0;
691 }
692
693 int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
694                         struct vidconsole_bbox *label_bbox)
695 {
696         switch (obj->type) {
697         case SCENEOBJT_NONE:
698         case SCENEOBJT_IMAGE:
699         case SCENEOBJT_TEXT:
700                 return -ENOSYS;
701         case SCENEOBJT_MENU: {
702                 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
703
704                 scene_menu_calc_bbox(menu, bbox, label_bbox);
705                 break;
706         }
707         case SCENEOBJT_TEXTLINE: {
708                 struct scene_obj_textline *tline;
709
710                 tline = (struct scene_obj_textline *)obj;
711                 scene_textline_calc_bbox(tline, bbox, label_bbox);
712                 break;
713         }
714         }
715
716         return 0;
717 }
718
719 int scene_calc_dims(struct scene *scn, bool do_menus)
720 {
721         struct scene_obj *obj;
722         int ret;
723
724         list_for_each_entry(obj, &scn->obj_head, sibling) {
725                 switch (obj->type) {
726                 case SCENEOBJT_NONE:
727                 case SCENEOBJT_TEXT:
728                 case SCENEOBJT_IMAGE: {
729                         int width;
730
731                         if (!do_menus) {
732                                 ret = scene_obj_get_hw(scn, obj->id, &width);
733                                 if (ret < 0)
734                                         return log_msg_ret("get", ret);
735                                 obj->dim.w = width;
736                                 obj->dim.h = ret;
737                         }
738                         break;
739                 }
740                 case SCENEOBJT_MENU: {
741                         struct scene_obj_menu *menu;
742
743                         if (do_menus) {
744                                 menu = (struct scene_obj_menu *)obj;
745
746                                 ret = scene_menu_calc_dims(menu);
747                                 if (ret)
748                                         return log_msg_ret("men", ret);
749                         }
750                         break;
751                 }
752                 case SCENEOBJT_TEXTLINE: {
753                         struct scene_obj_textline *tline;
754
755                         tline = (struct scene_obj_textline *)obj;
756                         ret = scene_textline_calc_dims(tline);
757                         if (ret)
758                                 return log_msg_ret("men", ret);
759
760                         break;
761                 }
762                 }
763         }
764
765         return 0;
766 }
767
768 int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
769 {
770         struct scene_obj *obj;
771         int ret;
772
773         /* Avoid error-checking optional items */
774         scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
775
776         list_for_each_entry(obj, &scn->obj_head, sibling) {
777                 switch (obj->type) {
778                 case SCENEOBJT_NONE:
779                 case SCENEOBJT_IMAGE:
780                 case SCENEOBJT_MENU:
781                 case SCENEOBJT_TEXTLINE:
782                         break;
783                 case SCENEOBJT_TEXT:
784                         scene_txt_set_font(scn, obj->id, NULL,
785                                            theme->font_size);
786                         break;
787                 }
788         }
789
790         ret = scene_arrange(scn);
791         if (ret)
792                 return log_msg_ret("arr", ret);
793
794         return 0;
795 }
796
797 void scene_set_highlight_id(struct scene *scn, uint id)
798 {
799         scn->highlight_id = id;
800 }
801
802 void scene_highlight_first(struct scene *scn)
803 {
804         struct scene_obj *obj;
805
806         list_for_each_entry(obj, &scn->obj_head, sibling) {
807                 if (scene_obj_can_highlight(obj)) {
808                         scene_set_highlight_id(scn, obj->id);
809                         return;
810                 }
811         }
812 }
813
814 static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
815 {
816         int ret;
817
818         switch (obj->type) {
819         case SCENEOBJT_NONE:
820         case SCENEOBJT_IMAGE:
821         case SCENEOBJT_MENU:
822         case SCENEOBJT_TEXT:
823                 break;
824         case SCENEOBJT_TEXTLINE:
825                 ret = scene_textline_open(scn,
826                                           (struct scene_obj_textline *)obj);
827                 if (ret)
828                         return log_msg_ret("op", ret);
829                 break;
830         }
831
832         return 0;
833 }
834
835 int scene_set_open(struct scene *scn, uint id, bool open)
836 {
837         struct scene_obj *obj;
838         int ret;
839
840         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
841         if (!obj)
842                 return log_msg_ret("find", -ENOENT);
843
844         if (open) {
845                 ret = scene_obj_open(scn, obj);
846                 if (ret)
847                         return log_msg_ret("op", ret);
848         }
849
850         ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
851                                     open ? SCENEOF_OPEN : 0);
852         if (ret)
853                 return log_msg_ret("flg", ret);
854
855         return 0;
856 }
857
858 int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
859                     void *priv)
860 {
861         struct scene_obj *obj;
862
863         list_for_each_entry(obj, &scn->obj_head, sibling) {
864                 int ret;
865
866                 ret = iter(obj, priv);
867                 if (ret)
868                         return log_msg_ret("itr", ret);
869         }
870
871         return 0;
872 }
873
874 int scene_bbox_union(struct scene *scn, uint id, int inset,
875                      struct vidconsole_bbox *bbox)
876 {
877         struct scene_obj *obj;
878
879         if (!id)
880                 return 0;
881         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
882         if (!obj)
883                 return log_msg_ret("obj", -ENOENT);
884         if (bbox->valid) {
885                 bbox->x0 = min(bbox->x0, obj->dim.x - inset);
886                 bbox->y0 = min(bbox->y0, obj->dim.y);
887                 bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
888                 bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
889         } else {
890                 bbox->x0 = obj->dim.x - inset;
891                 bbox->y0 = obj->dim.y;
892                 bbox->x1 = obj->dim.x + obj->dim.w + inset;
893                 bbox->y1 = obj->dim.y + obj->dim.h;
894                 bbox->valid = true;
895         }
896
897         return 0;
898 }