blob: 3290a40222afa1c7c23aeb2e7e51ea6efc99b848 [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 Glassa968f5f2023-10-01 19:13:31 -060034 abuf_init(&scn->buf);
35 if (!abuf_realloc(&scn->buf, EXPO_MAX_CHARS + 1)) {
36 free(scn->name);
37 free(scn);
38 return log_msg_ret("buf", -ENOMEM);
39 }
40 abuf_init(&scn->entry_save);
41
Simon Glass0a4d14b2023-01-06 08:52:37 -060042 INIT_LIST_HEAD(&scn->obj_head);
43 scn->id = resolve_id(exp, id);
44 scn->expo = exp;
45 list_add_tail(&scn->sibling, &exp->scene_head);
46
47 *scnp = scn;
48
49 return scn->id;
50}
51
52void scene_obj_destroy(struct scene_obj *obj)
53{
54 if (obj->type == SCENEOBJT_MENU)
55 scene_menu_destroy((struct scene_obj_menu *)obj);
56 free(obj->name);
57 free(obj);
58}
59
60void scene_destroy(struct scene *scn)
61{
62 struct scene_obj *obj, *next;
63
64 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
65 scene_obj_destroy(obj);
66
Simon Glassa968f5f2023-10-01 19:13:31 -060067 abuf_uninit(&scn->entry_save);
68 abuf_uninit(&scn->buf);
Simon Glass0a4d14b2023-01-06 08:52:37 -060069 free(scn->name);
Simon Glass0a4d14b2023-01-06 08:52:37 -060070 free(scn);
71}
72
Simon Glass0a4d14b2023-01-06 08:52:37 -060073int scene_obj_count(struct scene *scn)
74{
Sughosh Ganuebb1c202024-08-28 22:24:22 +053075 return list_count_nodes(&scn->obj_head);
Simon Glass0a4d14b2023-01-06 08:52:37 -060076}
77
Simon Glass45ff0bc2023-08-14 16:40:21 -060078void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
Simon Glass0a4d14b2023-01-06 08:52:37 -060079{
80 struct scene_obj *obj;
81
82 list_for_each_entry(obj, &scn->obj_head, sibling) {
83 if (obj->id == id &&
84 (type == SCENEOBJT_NONE || obj->type == type))
85 return obj;
86 }
87
88 return NULL;
89}
90
Simon Glassc8925112023-06-01 10:23:02 -060091void *scene_obj_find_by_name(struct scene *scn, const char *name)
92{
93 struct scene_obj *obj;
94
95 list_for_each_entry(obj, &scn->obj_head, sibling) {
96 if (!strcmp(name, obj->name))
97 return obj;
98 }
99
100 return NULL;
101}
102
Simon Glass0a4d14b2023-01-06 08:52:37 -0600103int scene_obj_add(struct scene *scn, const char *name, uint id,
104 enum scene_obj_t type, uint size, struct scene_obj **objp)
105{
106 struct scene_obj *obj;
107
108 obj = calloc(1, size);
109 if (!obj)
110 return log_msg_ret("obj", -ENOMEM);
111 obj->name = strdup(name);
112 if (!obj->name) {
113 free(obj);
114 return log_msg_ret("name", -ENOMEM);
115 }
116
117 obj->id = resolve_id(scn->expo, id);
118 obj->scene = scn;
119 obj->type = type;
120 list_add_tail(&obj->sibling, &scn->obj_head);
121 *objp = obj;
122
123 return obj->id;
124}
125
126int scene_img(struct scene *scn, const char *name, uint id, char *data,
127 struct scene_obj_img **imgp)
128{
129 struct scene_obj_img *img;
130 int ret;
131
132 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
133 sizeof(struct scene_obj_img),
134 (struct scene_obj **)&img);
135 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600136 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600137
138 img->data = data;
139
140 if (imgp)
141 *imgp = img;
142
143 return img->obj.id;
144}
145
146int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
147 struct scene_obj_txt **txtp)
148{
149 struct scene_obj_txt *txt;
150 int ret;
151
152 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
153 sizeof(struct scene_obj_txt),
154 (struct scene_obj **)&txt);
155 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600156 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600157
158 txt->str_id = str_id;
159
160 if (txtp)
161 *txtp = txt;
162
163 return txt->obj.id;
164}
165
166int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
167 const char *str, struct scene_obj_txt **txtp)
168{
169 struct scene_obj_txt *txt;
170 int ret;
171
172 ret = expo_str(scn->expo, name, str_id, str);
173 if (ret < 0)
174 return log_msg_ret("str", ret);
Simon Glass51bde1a2023-10-01 19:13:26 -0600175 if (str_id && ret != str_id)
Simon Glass0a4d14b2023-01-06 08:52:37 -0600176 return log_msg_ret("id", -EEXIST);
Simon Glass51bde1a2023-10-01 19:13:26 -0600177 str_id = ret;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600178
179 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
180 sizeof(struct scene_obj_txt),
181 (struct scene_obj **)&txt);
182 if (ret < 0)
Simon Glass1b4a2252023-10-01 19:13:25 -0600183 return log_msg_ret("obj", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600184
185 txt->str_id = str_id;
186
187 if (txtp)
188 *txtp = txt;
189
190 return txt->obj.id;
191}
192
193int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
194 uint font_size)
195{
196 struct scene_obj_txt *txt;
197
198 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
199 if (!txt)
200 return log_msg_ret("find", -ENOENT);
201 txt->font_name = font_name;
202 txt->font_size = font_size;
203
204 return 0;
205}
206
207int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
208{
209 struct scene_obj *obj;
210
211 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
212 if (!obj)
213 return log_msg_ret("find", -ENOENT);
Simon Glass7b043952023-06-01 10:22:49 -0600214 obj->dim.x = x;
215 obj->dim.y = y;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600216
217 return 0;
218}
219
Simon Glass7a960052023-06-01 10:22:52 -0600220int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
221{
222 struct scene_obj *obj;
223
224 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
225 if (!obj)
226 return log_msg_ret("find", -ENOENT);
227 obj->dim.w = w;
228 obj->dim.h = h;
229
230 return 0;
231}
232
Simon Glass0a4d14b2023-01-06 08:52:37 -0600233int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
234{
Simon Glass6081b0f2023-06-01 10:22:50 -0600235 int ret;
236
237 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
238 hide ? SCENEOF_HIDE : 0);
239 if (ret)
240 return log_msg_ret("flg", ret);
241
242 return 0;
243}
244
245int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
246{
Simon Glass0a4d14b2023-01-06 08:52:37 -0600247 struct scene_obj *obj;
248
249 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
250 if (!obj)
251 return log_msg_ret("find", -ENOENT);
Simon Glass6081b0f2023-06-01 10:22:50 -0600252 obj->flags &= ~clr;
253 obj->flags |= set;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600254
255 return 0;
256}
257
258int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
259{
260 struct scene_obj *obj;
261
262 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
263 if (!obj)
264 return log_msg_ret("find", -ENOENT);
265
266 switch (obj->type) {
267 case SCENEOBJT_NONE:
268 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600269 case SCENEOBJT_TEXTLINE:
Simon Glass0a4d14b2023-01-06 08:52:37 -0600270 break;
271 case SCENEOBJT_IMAGE: {
272 struct scene_obj_img *img = (struct scene_obj_img *)obj;
273 ulong width, height;
274 uint bpix;
275
276 video_bmp_get_info(img->data, &width, &height, &bpix);
277 if (widthp)
278 *widthp = width;
279 return height;
280 }
281 case SCENEOBJT_TEXT: {
282 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
283 struct expo *exp = scn->expo;
Simon Glass9e1a86d2023-06-01 10:22:51 -0600284 struct vidconsole_bbox bbox;
285 const char *str;
286 int len, ret;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600287
Simon Glass9e1a86d2023-06-01 10:22:51 -0600288 str = expo_get_str(exp, txt->str_id);
289 if (!str)
290 return log_msg_ret("str", -ENOENT);
291 len = strlen(str);
292
293 /* if there is no console, make it up */
294 if (!exp->cons) {
295 if (widthp)
296 *widthp = 8 * len;
297 return 16;
298 }
299
300 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
301 txt->font_size, str, &bbox);
302 if (ret)
303 return log_msg_ret("mea", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600304 if (widthp)
Simon Glass9e1a86d2023-06-01 10:22:51 -0600305 *widthp = bbox.x1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600306
Simon Glass9e1a86d2023-06-01 10:22:51 -0600307 return bbox.y1;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600308 }
309 }
310
311 return 0;
312}
313
314/**
Simon Glass118a7272023-10-01 19:13:30 -0600315 * scene_render_background() - Render the background for an object
316 *
317 * @obj: Object to render
Simon Glass0023d182023-10-01 19:13:34 -0600318 * @box_only: true to show a box around the object, but keep the normal
319 * background colour inside
Simon Glass118a7272023-10-01 19:13:30 -0600320 */
Simon Glass0023d182023-10-01 19:13:34 -0600321static void scene_render_background(struct scene_obj *obj, bool box_only)
Simon Glass118a7272023-10-01 19:13:30 -0600322{
323 struct expo *exp = obj->scene->expo;
324 const struct expo_theme *theme = &exp->theme;
325 struct vidconsole_bbox bbox, label_bbox;
326 struct udevice *dev = exp->display;
327 struct video_priv *vid_priv;
328 struct udevice *cons = exp->cons;
329 struct vidconsole_colour old;
330 enum colour_idx fore, back;
331 uint inset = theme->menu_inset;
332
333 /* draw a background for the object */
334 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
Simon Glassbda3adc2024-10-14 16:31:53 -0600335 fore = VID_DARK_GREY;
Simon Glass118a7272023-10-01 19:13:30 -0600336 back = VID_WHITE;
337 } else {
338 fore = VID_LIGHT_GRAY;
339 back = VID_BLACK;
340 }
341
342 /* see if this object wants to render a background */
343 if (scene_obj_calc_bbox(obj, &bbox, &label_bbox))
344 return;
345
346 vidconsole_push_colour(cons, fore, back, &old);
347 vid_priv = dev_get_uclass_priv(dev);
348 video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset,
349 label_bbox.x1 + inset, label_bbox.y1 + inset,
350 vid_priv->colour_fg);
351 vidconsole_pop_colour(cons, &old);
Simon Glass0023d182023-10-01 19:13:34 -0600352 if (box_only) {
353 video_fill_part(dev, label_bbox.x0, label_bbox.y0,
354 label_bbox.x1, label_bbox.y1,
355 vid_priv->colour_bg);
356 }
Simon Glass118a7272023-10-01 19:13:30 -0600357}
358
359/**
Simon Glass0a4d14b2023-01-06 08:52:37 -0600360 * scene_obj_render() - Render an object
361 *
362 */
363static int scene_obj_render(struct scene_obj *obj, bool text_mode)
364{
365 struct scene *scn = obj->scene;
366 struct expo *exp = scn->expo;
Simon Glass86f1ac52023-06-01 10:23:00 -0600367 const struct expo_theme *theme = &exp->theme;
Simon Glass67e2af12023-06-01 10:22:34 -0600368 struct udevice *dev = exp->display;
369 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600370 int x, y, ret;
371
Simon Glass7b043952023-06-01 10:22:49 -0600372 x = obj->dim.x;
373 y = obj->dim.y;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600374
375 switch (obj->type) {
376 case SCENEOBJT_NONE:
377 break;
378 case SCENEOBJT_IMAGE: {
379 struct scene_obj_img *img = (struct scene_obj_img *)obj;
380
381 if (!cons)
382 return -ENOTSUPP;
383 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
384 true);
385 if (ret < 0)
386 return log_msg_ret("img", ret);
387 break;
388 }
389 case SCENEOBJT_TEXT: {
390 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
391 const char *str;
392
393 if (!cons)
394 return -ENOTSUPP;
395
396 if (txt->font_name || txt->font_size) {
397 ret = vidconsole_select_font(cons,
398 txt->font_name,
399 txt->font_size);
400 } else {
401 ret = vidconsole_select_font(cons, NULL, 0);
402 }
403 if (ret && ret != -ENOSYS)
404 return log_msg_ret("font", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600405 str = expo_get_str(exp, txt->str_id);
Simon Glass01922ec2023-06-01 10:22:57 -0600406 if (str) {
407 struct video_priv *vid_priv;
408 struct vidconsole_colour old;
409 enum colour_idx fore, back;
410
411 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
412 fore = VID_BLACK;
413 back = VID_WHITE;
414 } else {
415 fore = VID_LIGHT_GRAY;
416 back = VID_BLACK;
417 }
418
419 vid_priv = dev_get_uclass_priv(dev);
420 if (obj->flags & SCENEOF_POINT) {
421 vidconsole_push_colour(cons, fore, back, &old);
Simon Glass86f1ac52023-06-01 10:23:00 -0600422 video_fill_part(dev, x - theme->menu_inset, y,
423 x + obj->dim.w,
424 y + obj->dim.h,
Simon Glass01922ec2023-06-01 10:22:57 -0600425 vid_priv->colour_bg);
426 }
427 vidconsole_set_cursor_pos(cons, x, y);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600428 vidconsole_put_string(cons, str);
Simon Glass01922ec2023-06-01 10:22:57 -0600429 if (obj->flags & SCENEOF_POINT)
430 vidconsole_pop_colour(cons, &old);
431 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600432 break;
433 }
434 case SCENEOBJT_MENU: {
435 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600436
437 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
438 if (!cons)
439 return -ENOTSUPP;
440
441 /* draw a background behind the menu items */
Simon Glass0023d182023-10-01 19:13:34 -0600442 scene_render_background(obj, false);
Simon Glass01922ec2023-06-01 10:22:57 -0600443 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600444 /*
445 * With a vidconsole, the text and item pointer are rendered as
446 * normal objects so we don't need to do anything here. The menu
447 * simply controls where they are positioned.
448 */
449 if (cons)
450 return -ENOTSUPP;
451
452 ret = scene_menu_display(menu);
453 if (ret < 0)
454 return log_msg_ret("img", ret);
455
456 break;
457 }
Simon Glass0023d182023-10-01 19:13:34 -0600458 case SCENEOBJT_TEXTLINE:
459 if (obj->flags & SCENEOF_OPEN)
460 scene_render_background(obj, true);
461 break;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600462 }
463
464 return 0;
465}
466
Simon Glass377f18e2024-10-14 16:31:55 -0600467int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
468{
469 struct scene_obj *obj;
470
471 arr->label_width = 0;
472 list_for_each_entry(obj, &scn->obj_head, sibling) {
473 uint label_id = 0;
474 int width;
475
476 switch (obj->type) {
477 case SCENEOBJT_NONE:
478 case SCENEOBJT_IMAGE:
479 case SCENEOBJT_TEXT:
480 break;
481 case SCENEOBJT_MENU: {
482 struct scene_obj_menu *menu;
483
484 menu = (struct scene_obj_menu *)obj,
485 label_id = menu->title_id;
486 break;
487 }
488 case SCENEOBJT_TEXTLINE: {
489 struct scene_obj_textline *tline;
490
491 tline = (struct scene_obj_textline *)obj,
492 label_id = tline->label_id;
493 break;
494 }
495 }
496
497 if (label_id) {
498 int ret;
499
500 ret = scene_obj_get_hw(scn, label_id, &width);
501 if (ret < 0)
502 return log_msg_ret("hei", ret);
503 arr->label_width = max(arr->label_width, width);
504 }
505 }
506
507 return 0;
508}
509
Simon Glass0a4d14b2023-01-06 08:52:37 -0600510int scene_arrange(struct scene *scn)
511{
Simon Glass377f18e2024-10-14 16:31:55 -0600512 struct expo_arrange_info arr;
Simon Glass0a4d14b2023-01-06 08:52:37 -0600513 struct scene_obj *obj;
514 int ret;
515
Simon Glass377f18e2024-10-14 16:31:55 -0600516 ret = scene_calc_arrange(scn, &arr);
517 if (ret < 0)
518 return log_msg_ret("arr", ret);
519
Simon Glass0a4d14b2023-01-06 08:52:37 -0600520 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glassb7a64532023-10-01 19:13:24 -0600521 switch (obj->type) {
522 case SCENEOBJT_NONE:
523 case SCENEOBJT_IMAGE:
524 case SCENEOBJT_TEXT:
525 break;
526 case SCENEOBJT_MENU: {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600527 struct scene_obj_menu *menu;
528
529 menu = (struct scene_obj_menu *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600530 ret = scene_menu_arrange(scn, &arr, menu);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600531 if (ret)
532 return log_msg_ret("arr", ret);
Simon Glassb7a64532023-10-01 19:13:24 -0600533 break;
534 }
Simon Glass0023d182023-10-01 19:13:34 -0600535 case SCENEOBJT_TEXTLINE: {
536 struct scene_obj_textline *tline;
537
538 tline = (struct scene_obj_textline *)obj,
Simon Glass377f18e2024-10-14 16:31:55 -0600539 ret = scene_textline_arrange(scn, &arr, tline);
Simon Glass0023d182023-10-01 19:13:34 -0600540 if (ret)
541 return log_msg_ret("arr", ret);
542 break;
543 }
Simon Glass0a4d14b2023-01-06 08:52:37 -0600544 }
545 }
546
547 return 0;
548}
549
Simon Glass12f57732023-06-01 10:22:58 -0600550int scene_render_deps(struct scene *scn, uint id)
551{
552 struct scene_obj *obj;
553 int ret;
554
555 if (!id)
556 return 0;
557 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
558 if (!obj)
559 return log_msg_ret("obj", -ENOENT);
560
561 if (!(obj->flags & SCENEOF_HIDE)) {
562 ret = scene_obj_render(obj, false);
563 if (ret && ret != -ENOTSUPP)
564 return log_msg_ret("ren", ret);
565
Simon Glassb7a64532023-10-01 19:13:24 -0600566 switch (obj->type) {
567 case SCENEOBJT_NONE:
568 case SCENEOBJT_IMAGE:
569 case SCENEOBJT_TEXT:
570 break;
571 case SCENEOBJT_MENU:
Simon Glass12f57732023-06-01 10:22:58 -0600572 scene_menu_render_deps(scn,
573 (struct scene_obj_menu *)obj);
Simon Glassb7a64532023-10-01 19:13:24 -0600574 break;
Simon Glass0023d182023-10-01 19:13:34 -0600575 case SCENEOBJT_TEXTLINE:
576 scene_textline_render_deps(scn,
577 (struct scene_obj_textline *)obj);
578 break;
Simon Glassb7a64532023-10-01 19:13:24 -0600579 }
Simon Glass12f57732023-06-01 10:22:58 -0600580 }
581
582 return 0;
583}
584
Simon Glass0a4d14b2023-01-06 08:52:37 -0600585int scene_render(struct scene *scn)
586{
587 struct expo *exp = scn->expo;
588 struct scene_obj *obj;
589 int ret;
590
591 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass6081b0f2023-06-01 10:22:50 -0600592 if (!(obj->flags & SCENEOF_HIDE)) {
Simon Glass0a4d14b2023-01-06 08:52:37 -0600593 ret = scene_obj_render(obj, exp->text_mode);
594 if (ret && ret != -ENOTSUPP)
595 return log_msg_ret("ren", ret);
596 }
597 }
598
Simon Glass12f57732023-06-01 10:22:58 -0600599 /* render any highlighted object on top of the others */
600 if (scn->highlight_id && !exp->text_mode) {
601 ret = scene_render_deps(scn, scn->highlight_id);
602 if (ret && ret != -ENOTSUPP)
603 return log_msg_ret("dep", ret);
604 }
605
Simon Glass0a4d14b2023-01-06 08:52:37 -0600606 return 0;
607}
608
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600609/**
610 * send_key_obj() - Handle a keypress for moving between objects
611 *
612 * @scn: Scene to receive the key
613 * @key: Key to send (KEYCODE_UP)
614 * @event: Returns resulting event from this keypress
615 * Returns: 0 if OK, -ve on error
616 */
617static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
618 struct expo_action *event)
619{
620 switch (key) {
621 case BKEY_UP:
622 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
623 sibling)) {
624 obj = list_entry(obj->sibling.prev,
625 struct scene_obj, sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600626 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600627 event->type = EXPOACT_POINT_OBJ;
628 event->select.id = obj->id;
629 log_debug("up to obj %d\n", event->select.id);
630 break;
631 }
632 }
633 break;
634 case BKEY_DOWN:
635 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
636 obj = list_entry(obj->sibling.next, struct scene_obj,
637 sibling);
Simon Glass193bfea2023-10-01 19:13:27 -0600638 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600639 event->type = EXPOACT_POINT_OBJ;
640 event->select.id = obj->id;
641 log_debug("down to obj %d\n", event->select.id);
642 break;
643 }
644 }
645 break;
646 case BKEY_SELECT:
Simon Glass193bfea2023-10-01 19:13:27 -0600647 if (scene_obj_can_highlight(obj)) {
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600648 event->type = EXPOACT_OPEN;
649 event->select.id = obj->id;
650 log_debug("open obj %d\n", event->select.id);
651 }
652 break;
653 case BKEY_QUIT:
654 event->type = EXPOACT_QUIT;
655 log_debug("obj quit\n");
656 break;
657 }
658}
659
Simon Glass0a4d14b2023-01-06 08:52:37 -0600660int scene_send_key(struct scene *scn, int key, struct expo_action *event)
661{
662 struct scene_obj *obj;
663 int ret;
664
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600665 event->type = EXPOACT_NONE;
666
667 /*
668 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
669 * opened
670 */
671 if (scn->expo->popup) {
672 obj = NULL;
673 if (scn->highlight_id) {
674 obj = scene_obj_find(scn, scn->highlight_id,
675 SCENEOBJT_NONE);
676 }
677 if (!obj)
678 return 0;
679
680 if (!(obj->flags & SCENEOF_OPEN)) {
681 send_key_obj(scn, obj, key, event);
682 return 0;
683 }
684
Simon Glassb7a64532023-10-01 19:13:24 -0600685 switch (obj->type) {
686 case SCENEOBJT_NONE:
687 case SCENEOBJT_IMAGE:
688 case SCENEOBJT_TEXT:
689 break;
690 case SCENEOBJT_MENU: {
691 struct scene_obj_menu *menu;
692
693 menu = (struct scene_obj_menu *)obj,
694 ret = scene_menu_send_key(scn, menu, key, event);
695 if (ret)
696 return log_msg_ret("key", ret);
697 break;
698 }
Simon Glass0023d182023-10-01 19:13:34 -0600699 case SCENEOBJT_TEXTLINE: {
700 struct scene_obj_textline *tline;
701
702 tline = (struct scene_obj_textline *)obj,
703 ret = scene_textline_send_key(scn, tline, key, event);
704 if (ret)
705 return log_msg_ret("key", ret);
706 break;
707 }
Simon Glassb7a64532023-10-01 19:13:24 -0600708 }
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600709 return 0;
710 }
711
Simon Glass0a4d14b2023-01-06 08:52:37 -0600712 list_for_each_entry(obj, &scn->obj_head, sibling) {
713 if (obj->type == SCENEOBJT_MENU) {
714 struct scene_obj_menu *menu;
715
716 menu = (struct scene_obj_menu *)obj,
717 ret = scene_menu_send_key(scn, menu, key, event);
718 if (ret)
719 return log_msg_ret("key", ret);
Simon Glass0a4d14b2023-01-06 08:52:37 -0600720 break;
721 }
722 }
723
724 return 0;
725}
Simon Glass7a960052023-06-01 10:22:52 -0600726
Simon Glassf0994692023-10-01 19:13:29 -0600727int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox,
728 struct vidconsole_bbox *label_bbox)
729{
730 switch (obj->type) {
731 case SCENEOBJT_NONE:
732 case SCENEOBJT_IMAGE:
733 case SCENEOBJT_TEXT:
734 return -ENOSYS;
735 case SCENEOBJT_MENU: {
736 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
737
738 scene_menu_calc_bbox(menu, bbox, label_bbox);
739 break;
740 }
Simon Glass0023d182023-10-01 19:13:34 -0600741 case SCENEOBJT_TEXTLINE: {
742 struct scene_obj_textline *tline;
743
744 tline = (struct scene_obj_textline *)obj;
745 scene_textline_calc_bbox(tline, bbox, label_bbox);
746 break;
747 }
Simon Glassf0994692023-10-01 19:13:29 -0600748 }
749
750 return 0;
751}
752
Simon Glass7a960052023-06-01 10:22:52 -0600753int scene_calc_dims(struct scene *scn, bool do_menus)
754{
755 struct scene_obj *obj;
756 int ret;
757
758 list_for_each_entry(obj, &scn->obj_head, sibling) {
759 switch (obj->type) {
760 case SCENEOBJT_NONE:
761 case SCENEOBJT_TEXT:
762 case SCENEOBJT_IMAGE: {
763 int width;
764
765 if (!do_menus) {
766 ret = scene_obj_get_hw(scn, obj->id, &width);
767 if (ret < 0)
768 return log_msg_ret("get", ret);
769 obj->dim.w = width;
770 obj->dim.h = ret;
771 }
772 break;
773 }
774 case SCENEOBJT_MENU: {
775 struct scene_obj_menu *menu;
776
777 if (do_menus) {
778 menu = (struct scene_obj_menu *)obj;
779
780 ret = scene_menu_calc_dims(menu);
781 if (ret)
782 return log_msg_ret("men", ret);
783 }
784 break;
785 }
Simon Glass0023d182023-10-01 19:13:34 -0600786 case SCENEOBJT_TEXTLINE: {
787 struct scene_obj_textline *tline;
788
789 tline = (struct scene_obj_textline *)obj;
790 ret = scene_textline_calc_dims(tline);
791 if (ret)
792 return log_msg_ret("men", ret);
793
794 break;
795 }
Simon Glass7a960052023-06-01 10:22:52 -0600796 }
797 }
798
799 return 0;
800}
Simon Glassc999e172023-06-01 10:22:53 -0600801
802int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
803{
804 struct scene_obj *obj;
805 int ret;
806
807 /* Avoid error-checking optional items */
808 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
809
810 list_for_each_entry(obj, &scn->obj_head, sibling) {
811 switch (obj->type) {
812 case SCENEOBJT_NONE:
813 case SCENEOBJT_IMAGE:
814 case SCENEOBJT_MENU:
Simon Glass0023d182023-10-01 19:13:34 -0600815 case SCENEOBJT_TEXTLINE:
Simon Glassc999e172023-06-01 10:22:53 -0600816 break;
817 case SCENEOBJT_TEXT:
818 scene_txt_set_font(scn, obj->id, NULL,
819 theme->font_size);
820 break;
821 }
822 }
823
824 ret = scene_arrange(scn);
825 if (ret)
826 return log_msg_ret("arr", ret);
827
828 return 0;
829}
Simon Glass01922ec2023-06-01 10:22:57 -0600830
831void scene_set_highlight_id(struct scene *scn, uint id)
832{
833 scn->highlight_id = id;
834}
835
836void scene_highlight_first(struct scene *scn)
837{
838 struct scene_obj *obj;
839
840 list_for_each_entry(obj, &scn->obj_head, sibling) {
Simon Glass193bfea2023-10-01 19:13:27 -0600841 if (scene_obj_can_highlight(obj)) {
Simon Glass01922ec2023-06-01 10:22:57 -0600842 scene_set_highlight_id(scn, obj->id);
843 return;
Simon Glass01922ec2023-06-01 10:22:57 -0600844 }
845 }
846}
847
Simon Glassf6a943a2023-10-01 19:13:33 -0600848static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
849{
850 int ret;
851
852 switch (obj->type) {
853 case SCENEOBJT_NONE:
854 case SCENEOBJT_IMAGE:
855 case SCENEOBJT_MENU:
856 case SCENEOBJT_TEXT:
857 break;
858 case SCENEOBJT_TEXTLINE:
859 ret = scene_textline_open(scn,
860 (struct scene_obj_textline *)obj);
861 if (ret)
862 return log_msg_ret("op", ret);
863 break;
864 }
865
866 return 0;
867}
868
Simon Glass01922ec2023-06-01 10:22:57 -0600869int scene_set_open(struct scene *scn, uint id, bool open)
870{
Simon Glassf6a943a2023-10-01 19:13:33 -0600871 struct scene_obj *obj;
Simon Glass01922ec2023-06-01 10:22:57 -0600872 int ret;
873
Simon Glassf6a943a2023-10-01 19:13:33 -0600874 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
875 if (!obj)
876 return log_msg_ret("find", -ENOENT);
877
878 if (open) {
879 ret = scene_obj_open(scn, obj);
880 if (ret)
881 return log_msg_ret("op", ret);
882 }
883
Simon Glass01922ec2023-06-01 10:22:57 -0600884 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
885 open ? SCENEOF_OPEN : 0);
886 if (ret)
887 return log_msg_ret("flg", ret);
888
889 return 0;
890}
Simon Glasse90acd82023-08-14 16:40:23 -0600891
892int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
893 void *priv)
894{
895 struct scene_obj *obj;
896
897 list_for_each_entry(obj, &scn->obj_head, sibling) {
898 int ret;
899
900 ret = iter(obj, priv);
901 if (ret)
902 return log_msg_ret("itr", ret);
903 }
904
905 return 0;
906}
Simon Glassf0994692023-10-01 19:13:29 -0600907
908int scene_bbox_union(struct scene *scn, uint id, int inset,
909 struct vidconsole_bbox *bbox)
910{
911 struct scene_obj *obj;
912
913 if (!id)
914 return 0;
915 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
916 if (!obj)
917 return log_msg_ret("obj", -ENOENT);
918 if (bbox->valid) {
919 bbox->x0 = min(bbox->x0, obj->dim.x - inset);
920 bbox->y0 = min(bbox->y0, obj->dim.y);
921 bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
922 bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
923 } else {
924 bbox->x0 = obj->dim.x - inset;
925 bbox->y0 = obj->dim.y;
926 bbox->x1 = obj->dim.x + obj->dim.w + inset;
927 bbox->y1 = obj->dim.y + obj->dim.h;
928 bbox->valid = true;
929 }
930
931 return 0;
932}