blob: ea329252d28ab884485e3e1859e55db537efaeef [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 Glassc6143dc2025-05-02 08:46:35 -0600238int scene_obj_set_width(struct scene *scn, uint id, int w)
239{
240 struct scene_obj *obj;
241
242 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
243 if (!obj)
244 return log_msg_ret("find", -ENOENT);
245 obj->bbox.x1 = obj->bbox.x0 + w;
246
247 return 0;
248}
249
250int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1,
251 int y1)
252{
253 struct scene_obj *obj;
254
255 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
256 if (!obj)
257 return log_msg_ret("find", -ENOENT);
258 obj->bbox.x0 = x0;
259 obj->bbox.y0 = y0;
260 obj->bbox.x1 = x1;
261 obj->bbox.y1 = y1;
262 obj->flags |= SCENEOF_SIZE_VALID;
263
264 return 0;
265}
266
Simon Glass0a4d14b2023-01-06 08:52:37 -0600267int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
268{
Simon Glass6081b0f2023-06-01 10:22:50 -0600269 int ret;
270
271 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
272 hide ? SCENEOF_HIDE : 0);
273 if (ret)
274 return log_msg_ret("flg", ret);
275
276 return 0;
277}
278
279int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
280{
Simon Glass0a4d14b2023-01-06 08:52:37 -0600281 struct scene_obj *obj;
282
283 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
284 if (!obj)
285 return log_msg_ret("find", -ENOENT);
Simon Glass6081b0f2023-06-01 10:22:50 -0600286 obj->flags &= ~clr;
287 obj->flags |= set;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600288
289 return 0;
290}
291
292int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
293{
294 struct scene_obj *obj;
295
296 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
297 if (!obj)
298 return log_msg_ret("find", -ENOENT);
299
300 switch (obj->type) {
301 case SCENEOBJT_NONE:
302 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600303 case SCENEOBJT_TEXTLINE:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600304 break;
305 case SCENEOBJT_IMAGE: {
306 struct scene_obj_img *img = (struct scene_obj_img *)obj;
307 ulong width, height;
308 uint bpix;
309
310 video_bmp_get_info(img->data, &width, &height, &bpix);
311 if (widthp)
312 *widthp = width;
313 return height;
314 }
315 case SCENEOBJT_TEXT: {
316 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
317 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600318 struct vidconsole_bbox bbox;
319 const char *str;
320 int len, ret;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600321
Simon Glass9e1a86d2023-06-01 10:22:51 -0600322 str = expo_get_str(exp, txt->str_id);
323 if (!str)
324 return log_msg_ret("str", -ENOENT);
325 len = strlen(str);
326
327 /* if there is no console, make it up */
328 if (!exp->cons) {
329 if (widthp)
330 *widthp = 8 * len;
331 return 16;
332 }
333
334 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
Simon Glass3aa33582025-04-02 06:29:39 +1300335 txt->font_size, str, -1, &bbox, NULL);
Simon Glass9e1a86d2023-06-01 10:22:51 -0600336 if (ret)
337 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600338 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600339 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600340
Simon Glass9e1a86d2023-06-01 10:22:51 -0600341 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600342 }
343 }
344
345 return 0;
346}
347
348/**
Simon Glass118a7272023-10-01 19:13:30 -0600349 * scene_render_background() - Render the background for an object
350 *
351 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600352 * @box_only: true to show a box around the object, but keep the normal
353 * background colour inside
Simon Glass118a7272023-10-01 19:13:30 -0600354 */
Simon Glass0023d182023-10-01 19:13:34 -0600355static void scene_render_background(struct scene_obj *obj, bool box_only)
Simon Glass118a7272023-10-01 19:13:30 -0600356{
Simon Glass96910ef2025-05-02 08:46:34 -0600357 struct vidconsole_bbox bbox[SCENEBB_count], *sel;
Simon Glass118a7272023-10-01 19:13:30 -0600358 struct expo *exp = obj->scene->expo;
359 const struct expo_theme *theme = &exp->theme;
Simon Glass118a7272023-10-01 19:13:30 -0600360 struct udevice *dev = exp->display;
361 struct video_priv *vid_priv;
362 struct udevice *cons = exp->cons;
363 struct vidconsole_colour old;
364 enum colour_idx fore, back;
365 uint inset = theme->menu_inset;
366
Simon Glass21320da2025-04-02 06:29:33 +1300367 vid_priv = dev_get_uclass_priv(dev);
Simon Glass118a7272023-10-01 19:13:30 -0600368 /* draw a background for the object */
Simon Glass21320da2025-04-02 06:29:33 +1300369 if (vid_priv->white_on_black) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600370 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600371 back = VID_WHITE;
372 } else {
373 fore = VID_LIGHT_GRAY;
374 back = VID_BLACK;
375 }
376
377 /* see if this object wants to render a background */
Simon Glass96910ef2025-05-02 08:46:34 -0600378 if (scene_obj_calc_bbox(obj, bbox))
Simon Glass118a7272023-10-01 19:13:30 -0600379 return;
380
Simon Glass96910ef2025-05-02 08:46:34 -0600381 sel = &bbox[SCENEBB_label];
382 if (!sel->valid)
383 return;
384
Simon Glass118a7272023-10-01 19:13:30 -0600385 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass96910ef2025-05-02 08:46:34 -0600386 video_fill_part(dev, sel->x0 - inset, sel->y0 - inset,
387 sel->x1 + inset, sel->y1 + inset,
Simon Glass118a7272023-10-01 19:13:30 -0600388 vid_priv->colour_fg);
389 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600390 if (box_only) {
Simon Glass96910ef2025-05-02 08:46:34 -0600391 video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1,
Simon Glass0023d182023-10-01 19:13:34 -0600392 vid_priv->colour_bg);
393 }
Simon Glass118a7272023-10-01 19:13:30 -0600394}
395
396/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600397 * scene_obj_render() - Render an object
398 *
399 */
400static int scene_obj_render(struct scene_obj *obj, bool text_mode)
401{
402 struct scene *scn = obj->scene;
403 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600404 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600405 struct udevice *dev = exp->display;
406 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600407 int x, y, ret;
408
Simon Glassbc3a15f2025-05-02 08:46:31 -0600409 x = obj->bbox.x0;
410 y = obj->bbox.y0;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600411
412 switch (obj->type) {
413 case SCENEOBJT_NONE:
414 break;
415 case SCENEOBJT_IMAGE: {
416 struct scene_obj_img *img = (struct scene_obj_img *)obj;
417
418 if (!cons)
419 return -ENOTSUPP;
420 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
421 true);
422 if (ret < 0)
423 return log_msg_ret("img", ret);
424 break;
425 }
426 case SCENEOBJT_TEXT: {
427 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
428 const char *str;
429
430 if (!cons)
431 return -ENOTSUPP;
432
433 if (txt->font_name || txt->font_size) {
434 ret = vidconsole_select_font(cons,
435 txt->font_name,
436 txt->font_size);
437 } else {
438 ret = vidconsole_select_font(cons, NULL, 0);
439 }
440 if (ret && ret != -ENOSYS)
441 return log_msg_ret("font", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600442 str = expo_get_str(exp, txt->str_id);
Simon Glass01922ec2023-06-01 10:22:57 -0600443 if (str) {
444 struct video_priv *vid_priv;
445 struct vidconsole_colour old;
446 enum colour_idx fore, back;
447
Simon Glass21320da2025-04-02 06:29:33 +1300448 vid_priv = dev_get_uclass_priv(dev);
449 if (vid_priv->white_on_black) {
Simon Glass01922ec2023-06-01 10:22:57 -0600450 fore = VID_BLACK;
451 back = VID_WHITE;
452 } else {
453 fore = VID_LIGHT_GRAY;
454 back = VID_BLACK;
455 }
456
Simon Glass01922ec2023-06-01 10:22:57 -0600457 if (obj->flags & SCENEOF_POINT) {
458 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass86f1ac52023-06-01 10:23:00 -0600459 video_fill_part(dev, x - theme->menu_inset, y,
Simon Glassebec4972025-05-02 08:46:33 -0600460 obj->bbox.x1,
461 obj->bbox.y1,
Simon Glass01922ec2023-06-01 10:22:57 -0600462 vid_priv->colour_bg);
463 }
464 vidconsole_set_cursor_pos(cons, x, y);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600465 vidconsole_put_string(cons, str);
Simon Glass01922ec2023-06-01 10:22:57 -0600466 if (obj->flags & SCENEOF_POINT)
467 vidconsole_pop_colour(cons, &old);
468 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600469 break;
470 }
471 case SCENEOBJT_MENU: {
472 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600473
474 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
475 if (!cons)
476 return -ENOTSUPP;
477
478 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600479 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600480 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600481 /*
482 * With a vidconsole, the text and item pointer are rendered as
483 * normal objects so we don't need to do anything here. The menu
484 * simply controls where they are positioned.
485 */
486 if (cons)
487 return -ENOTSUPP;
488
489 ret = scene_menu_display(menu);
490 if (ret < 0)
491 return log_msg_ret("img", ret);
492
493 break;
494 }
Simon Glass0023d182023-10-01 19:13:34 -0600495 case SCENEOBJT_TEXTLINE:
496 if (obj->flags & SCENEOF_OPEN)
497 scene_render_background(obj, true);
498 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600499 }
500
501 return 0;
502}
503
Simon Glass377f18e2024-10-14 16:31:55 -0600504int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
505{
506 struct scene_obj *obj;
507
508 arr->label_width = 0;
509 list_for_each_entry(obj, &scn->obj_head, sibling) {
510 uint label_id = 0;
511 int width;
512
513 switch (obj->type) {
514 case SCENEOBJT_NONE:
515 case SCENEOBJT_IMAGE:
516 case SCENEOBJT_TEXT:
517 break;
518 case SCENEOBJT_MENU: {
519 struct scene_obj_menu *menu;
520
521 menu = (struct scene_obj_menu *)obj,
522 label_id = menu->title_id;
523 break;
524 }
525 case SCENEOBJT_TEXTLINE: {
526 struct scene_obj_textline *tline;
527
528 tline = (struct scene_obj_textline *)obj,
529 label_id = tline->label_id;
530 break;
531 }
532 }
533
534 if (label_id) {
535 int ret;
536
537 ret = scene_obj_get_hw(scn, label_id, &width);
538 if (ret < 0)
539 return log_msg_ret("hei", ret);
540 arr->label_width = max(arr->label_width, width);
541 }
542 }
543
544 return 0;
545}
546
Simon Glass0a4d14b2023-01-06 08:52:37 -0600547int scene_arrange(struct scene *scn)
548{
Simon Glass377f18e2024-10-14 16:31:55 -0600549 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600550 struct scene_obj *obj;
551 int ret;
552
Simon Glass377f18e2024-10-14 16:31:55 -0600553 ret = scene_calc_arrange(scn, &arr);
554 if (ret < 0)
555 return log_msg_ret("arr", ret);
556
Simon Glass0a4d14b2023-01-06 08:52:37 -0600557 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600558 switch (obj->type) {
559 case SCENEOBJT_NONE:
560 case SCENEOBJT_IMAGE:
561 case SCENEOBJT_TEXT:
562 break;
563 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600564 struct scene_obj_menu *menu;
565
566 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600567 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600568 if (ret)
569 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600570 break;
571 }
Simon Glass0023d182023-10-01 19:13:34 -0600572 case SCENEOBJT_TEXTLINE: {
573 struct scene_obj_textline *tline;
574
575 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600576 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600577 if (ret)
578 return log_msg_ret("arr", ret);
579 break;
580 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600581 }
582 }
583
584 return 0;
585}
586
Simon Glass12f57732023-06-01 10:22:58 -0600587int scene_render_deps(struct scene *scn, uint id)
588{
589 struct scene_obj *obj;
590 int ret;
591
592 if (!id)
593 return 0;
594 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
595 if (!obj)
596 return log_msg_ret("obj", -ENOENT);
597
598 if (!(obj->flags & SCENEOF_HIDE)) {
599 ret = scene_obj_render(obj, false);
600 if (ret && ret != -ENOTSUPP)
601 return log_msg_ret("ren", ret);
602
Simon Glassb7a64532023-10-01 19:13:24 -0600603 switch (obj->type) {
604 case SCENEOBJT_NONE:
605 case SCENEOBJT_IMAGE:
606 case SCENEOBJT_TEXT:
607 break;
608 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600609 scene_menu_render_deps(scn,
610 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600611 break;
Simon Glass0023d182023-10-01 19:13:34 -0600612 case SCENEOBJT_TEXTLINE:
613 scene_textline_render_deps(scn,
614 (struct scene_obj_textline *)obj);
615 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600616 }
Simon Glass12f57732023-06-01 10:22:58 -0600617 }
618
619 return 0;
620}
621
Simon Glass0a4d14b2023-01-06 08:52:37 -0600622int scene_render(struct scene *scn)
623{
624 struct expo *exp = scn->expo;
625 struct scene_obj *obj;
626 int ret;
627
628 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600629 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600630 ret = scene_obj_render(obj, exp->text_mode);
631 if (ret && ret != -ENOTSUPP)
632 return log_msg_ret("ren", ret);
633 }
634 }
635
Simon Glass12f57732023-06-01 10:22:58 -0600636 /* render any highlighted object on top of the others */
637 if (scn->highlight_id && !exp->text_mode) {
638 ret = scene_render_deps(scn, scn->highlight_id);
639 if (ret && ret != -ENOTSUPP)
640 return log_msg_ret("dep", ret);
641 }
642
Simon Glass0a4d14b2023-01-06 08:52:37 -0600643 return 0;
644}
645
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600646/**
647 * send_key_obj() - Handle a keypress for moving between objects
648 *
649 * @scn: Scene to receive the key
650 * @key: Key to send (KEYCODE_UP)
651 * @event: Returns resulting event from this keypress
652 * Returns: 0 if OK, -ve on error
653 */
654static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
655 struct expo_action *event)
656{
657 switch (key) {
658 case BKEY_UP:
659 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
660 sibling)) {
661 obj = list_entry(obj->sibling.prev,
662 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600663 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600664 event->type = EXPOACT_POINT_OBJ;
665 event->select.id = obj->id;
666 log_debug("up to obj %d\n", event->select.id);
667 break;
668 }
669 }
670 break;
671 case BKEY_DOWN:
672 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
673 obj = list_entry(obj->sibling.next, struct scene_obj,
674 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600675 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600676 event->type = EXPOACT_POINT_OBJ;
677 event->select.id = obj->id;
678 log_debug("down to obj %d\n", event->select.id);
679 break;
680 }
681 }
682 break;
683 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600684 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600685 event->type = EXPOACT_OPEN;
686 event->select.id = obj->id;
687 log_debug("open obj %d\n", event->select.id);
688 }
689 break;
690 case BKEY_QUIT:
691 event->type = EXPOACT_QUIT;
692 log_debug("obj quit\n");
693 break;
694 }
695}
696
Simon Glass0a4d14b2023-01-06 08:52:37 -0600697int scene_send_key(struct scene *scn, int key, struct expo_action *event)
698{
699 struct scene_obj *obj;
700 int ret;
701
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600702 event->type = EXPOACT_NONE;
703
704 /*
705 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
706 * opened
707 */
708 if (scn->expo->popup) {
709 obj = NULL;
710 if (scn->highlight_id) {
711 obj = scene_obj_find(scn, scn->highlight_id,
712 SCENEOBJT_NONE);
713 }
714 if (!obj)
715 return 0;
716
717 if (!(obj->flags & SCENEOF_OPEN)) {
718 send_key_obj(scn, obj, key, event);
719 return 0;
720 }
721
Simon Glassb7a64532023-10-01 19:13:24 -0600722 switch (obj->type) {
723 case SCENEOBJT_NONE:
724 case SCENEOBJT_IMAGE:
725 case SCENEOBJT_TEXT:
726 break;
727 case SCENEOBJT_MENU: {
728 struct scene_obj_menu *menu;
729
730 menu = (struct scene_obj_menu *)obj,
731 ret = scene_menu_send_key(scn, menu, key, event);
732 if (ret)
733 return log_msg_ret("key", ret);
734 break;
735 }
Simon Glass0023d182023-10-01 19:13:34 -0600736 case SCENEOBJT_TEXTLINE: {
737 struct scene_obj_textline *tline;
738
739 tline = (struct scene_obj_textline *)obj,
740 ret = scene_textline_send_key(scn, tline, key, event);
741 if (ret)
742 return log_msg_ret("key", ret);
743 break;
744 }
Simon Glassb7a64532023-10-01 19:13:24 -0600745 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600746 return 0;
747 }
748
Simon Glass0a4d14b2023-01-06 08:52:37 -0600749 list_for_each_entry(obj, &scn->obj_head, sibling) {
750 if (obj->type == SCENEOBJT_MENU) {
751 struct scene_obj_menu *menu;
752
753 menu = (struct scene_obj_menu *)obj,
754 ret = scene_menu_send_key(scn, menu, key, event);
755 if (ret)
756 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600757 break;
758 }
759 }
760
761 return 0;
762}
Simon Glass7a960052023-06-01 10:22:52 -0600763
Simon Glass96910ef2025-05-02 08:46:34 -0600764int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
Simon Glassf0994692023-10-01 19:13:29 -0600765{
766 switch (obj->type) {
767 case SCENEOBJT_NONE:
768 case SCENEOBJT_IMAGE:
769 case SCENEOBJT_TEXT:
770 return -ENOSYS;
771 case SCENEOBJT_MENU: {
772 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
773
Simon Glass96910ef2025-05-02 08:46:34 -0600774 scene_menu_calc_bbox(menu, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600775 break;
776 }
Simon Glass0023d182023-10-01 19:13:34 -0600777 case SCENEOBJT_TEXTLINE: {
778 struct scene_obj_textline *tline;
779
780 tline = (struct scene_obj_textline *)obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600781 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
782 &bbox[SCENEBB_label]);
Simon Glass0023d182023-10-01 19:13:34 -0600783 break;
784 }
Simon Glassf0994692023-10-01 19:13:29 -0600785 }
786
787 return 0;
788}
789
Simon Glass7a960052023-06-01 10:22:52 -0600790int scene_calc_dims(struct scene *scn, bool do_menus)
791{
792 struct scene_obj *obj;
793 int ret;
794
795 list_for_each_entry(obj, &scn->obj_head, sibling) {
796 switch (obj->type) {
797 case SCENEOBJT_NONE:
798 case SCENEOBJT_TEXT:
799 case SCENEOBJT_IMAGE: {
800 int width;
801
802 if (!do_menus) {
803 ret = scene_obj_get_hw(scn, obj->id, &width);
804 if (ret < 0)
805 return log_msg_ret("get", ret);
Simon Glassebec4972025-05-02 08:46:33 -0600806 obj->dims.x = width;
807 obj->dims.y = ret;
808 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
809 obj->bbox.x1 = obj->bbox.x0 + width;
810 obj->bbox.y1 = obj->bbox.y0 + ret;
811 obj->flags |= SCENEOF_SIZE_VALID;
812 }
Simon Glass7a960052023-06-01 10:22:52 -0600813 }
814 break;
815 }
816 case SCENEOBJT_MENU: {
817 struct scene_obj_menu *menu;
818
819 if (do_menus) {
820 menu = (struct scene_obj_menu *)obj;
821
822 ret = scene_menu_calc_dims(menu);
823 if (ret)
824 return log_msg_ret("men", ret);
825 }
826 break;
827 }
Simon Glass0023d182023-10-01 19:13:34 -0600828 case SCENEOBJT_TEXTLINE: {
829 struct scene_obj_textline *tline;
830
831 tline = (struct scene_obj_textline *)obj;
832 ret = scene_textline_calc_dims(tline);
833 if (ret)
834 return log_msg_ret("men", ret);
835
836 break;
837 }
Simon Glass7a960052023-06-01 10:22:52 -0600838 }
839 }
840
841 return 0;
842}
Simon Glassc999e172023-06-01 10:22:53 -0600843
844int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
845{
846 struct scene_obj *obj;
847 int ret;
848
849 /* Avoid error-checking optional items */
850 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
851
852 list_for_each_entry(obj, &scn->obj_head, sibling) {
853 switch (obj->type) {
854 case SCENEOBJT_NONE:
855 case SCENEOBJT_IMAGE:
856 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600857 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600858 break;
859 case SCENEOBJT_TEXT:
860 scene_txt_set_font(scn, obj->id, NULL,
861 theme->font_size);
862 break;
863 }
864 }
865
866 ret = scene_arrange(scn);
867 if (ret)
868 return log_msg_ret("arr", ret);
869
870 return 0;
871}
Simon Glass01922ec2023-06-01 10:22:57 -0600872
873void scene_set_highlight_id(struct scene *scn, uint id)
874{
875 scn->highlight_id = id;
876}
877
878void scene_highlight_first(struct scene *scn)
879{
880 struct scene_obj *obj;
881
882 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600883 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600884 scene_set_highlight_id(scn, obj->id);
885 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600886 }
887 }
888}
889
Simon Glassf6a943a2023-10-01 19:13:33 -0600890static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
891{
892 int ret;
893
894 switch (obj->type) {
895 case SCENEOBJT_NONE:
896 case SCENEOBJT_IMAGE:
897 case SCENEOBJT_MENU:
898 case SCENEOBJT_TEXT:
899 break;
900 case SCENEOBJT_TEXTLINE:
901 ret = scene_textline_open(scn,
902 (struct scene_obj_textline *)obj);
903 if (ret)
904 return log_msg_ret("op", ret);
905 break;
906 }
907
908 return 0;
909}
910
Simon Glass01922ec2023-06-01 10:22:57 -0600911int scene_set_open(struct scene *scn, uint id, bool open)
912{
Simon Glassf6a943a2023-10-01 19:13:33 -0600913 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600914 int ret;
915
Simon Glassf6a943a2023-10-01 19:13:33 -0600916 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
917 if (!obj)
918 return log_msg_ret("find", -ENOENT);
919
920 if (open) {
921 ret = scene_obj_open(scn, obj);
922 if (ret)
923 return log_msg_ret("op", ret);
924 }
925
Simon Glass01922ec2023-06-01 10:22:57 -0600926 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
927 open ? SCENEOF_OPEN : 0);
928 if (ret)
929 return log_msg_ret("flg", ret);
930
931 return 0;
932}
Simon Glasse90acd82023-08-14 16:40:23 -0600933
934int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
935 void *priv)
936{
937 struct scene_obj *obj;
938
939 list_for_each_entry(obj, &scn->obj_head, sibling) {
940 int ret;
941
942 ret = iter(obj, priv);
943 if (ret)
944 return log_msg_ret("itr", ret);
945 }
946
947 return 0;
948}
Simon Glassf0994692023-10-01 19:13:29 -0600949
Simon Glass96910ef2025-05-02 08:46:34 -0600950int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
951 struct vidconsole_bbox *dst)
952{
953 if (dst->valid) {
954 dst->x0 = min(dst->x0, src->x0 - inset);
955 dst->y0 = min(dst->y0, src->y0);
956 dst->x1 = max(dst->x1, src->x1 + inset);
957 dst->y1 = max(dst->y1, src->y1);
958 } else {
959 dst->x0 = src->x0 - inset;
960 dst->y0 = src->y0;
961 dst->x1 = src->x1 + inset;
962 dst->y1 = src->y1;
963 dst->valid = true;
964 }
965
966 return 0;
967}
968
Simon Glassf0994692023-10-01 19:13:29 -0600969int scene_bbox_union(struct scene *scn, uint id, int inset,
970 struct vidconsole_bbox *bbox)
971{
972 struct scene_obj *obj;
Simon Glass96910ef2025-05-02 08:46:34 -0600973 struct vidconsole_bbox local;
Simon Glassf0994692023-10-01 19:13:29 -0600974
975 if (!id)
976 return 0;
977 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
978 if (!obj)
979 return log_msg_ret("obj", -ENOENT);
Simon Glass96910ef2025-05-02 08:46:34 -0600980 local.x0 = obj->bbox.x0;
981 local.y0 = obj->bbox.y0;
982 local.x1 = obj->bbox.x1;
983 local.y1 = obj->bbox.y1;
984 local.valid = true;
985 scene_bbox_join(&local, inset, bbox);
Simon Glassf0994692023-10-01 19:13:29 -0600986
987 return 0;
988}