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 <alist.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
scene_new(struct expo * exp,const char * name,uint id,struct scene ** scnp)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 if (!abuf_init_size(&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
42 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
scene_obj_destroy(struct scene_obj * obj)52 void 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
scene_destroy(struct scene * scn)60 void 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
67 abuf_uninit(&scn->entry_save);
68 abuf_uninit(&scn->buf);
69 free(scn->name);
70 free(scn);
71 }
72
scene_obj_count(struct scene * scn)73 int scene_obj_count(struct scene *scn)
74 {
75 return list_count_nodes(&scn->obj_head);
76 }
77
scene_obj_find(const struct scene * scn,uint id,enum scene_obj_t type)78 void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type)
79 {
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
scene_obj_find_by_name(struct scene * scn,const char * name)91 void *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
scene_obj_add(struct scene * scn,const char * name,uint id,enum scene_obj_t type,uint size,struct scene_obj ** objp)103 int 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
scene_img(struct scene * scn,const char * name,uint id,char * data,struct scene_obj_img ** imgp)126 int 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)
136 return log_msg_ret("obj", ret);
137
138 img->data = data;
139
140 if (imgp)
141 *imgp = img;
142
143 return img->obj.id;
144 }
145
scene_txt_generic_init(struct expo * exp,struct scene_txt_generic * gen,const char * name,uint str_id,const char * str)146 int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen,
147 const char *name, uint str_id, const char *str)
148 {
149 int ret;
150
151 if (str) {
152 ret = expo_str(exp, name, str_id, str);
153 if (ret < 0)
154 return log_msg_ret("str", ret);
155 if (str_id && ret != str_id)
156 return log_msg_ret("id", -EEXIST);
157 str_id = ret;
158 } else {
159 ret = resolve_id(exp, str_id);
160 if (ret < 0)
161 return log_msg_ret("nst", ret);
162 if (str_id && ret != str_id)
163 return log_msg_ret("nid", -EEXIST);
164 }
165
166 gen->str_id = str_id;
167 alist_init_struct(&gen->lines, struct vidconsole_mline);
168
169 return 0;
170 }
171
scene_txt(struct scene * scn,const char * name,uint id,uint str_id,struct scene_obj_txt ** txtp)172 int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
173 struct scene_obj_txt **txtp)
174 {
175 struct scene_obj_txt *txt;
176 int ret;
177
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)
182 return log_msg_ret("obj", ret);
183
184 ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, NULL);
185 if (ret)
186 return log_msg_ret("stg", ret);
187 if (txtp)
188 *txtp = txt;
189
190 return txt->obj.id;
191 }
192
scene_txt_str(struct scene * scn,const char * name,uint id,uint str_id,const char * str,struct scene_obj_txt ** txtp)193 int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
194 const char *str, struct scene_obj_txt **txtp)
195 {
196 struct scene_obj_txt *txt;
197 int ret;
198
199 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
200 sizeof(struct scene_obj_txt),
201 (struct scene_obj **)&txt);
202 if (ret < 0)
203 return log_msg_ret("obj", ret);
204
205 ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, str);
206 if (ret)
207 return log_msg_ret("tsg", ret);
208 if (txtp)
209 *txtp = txt;
210
211 return txt->obj.id;
212 }
213
scene_box(struct scene * scn,const char * name,uint id,uint width,struct scene_obj_box ** boxp)214 int scene_box(struct scene *scn, const char *name, uint id, uint width,
215 struct scene_obj_box **boxp)
216 {
217 struct scene_obj_box *box;
218 int ret;
219
220 ret = scene_obj_add(scn, name, id, SCENEOBJT_BOX,
221 sizeof(struct scene_obj_box),
222 (struct scene_obj **)&box);
223 if (ret < 0)
224 return log_msg_ret("obj", ret);
225
226 box->width = width;
227
228 if (boxp)
229 *boxp = box;
230
231 return box->obj.id;
232 }
233
scene_txt_set_font(struct scene * scn,uint id,const char * font_name,uint font_size)234 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
235 uint font_size)
236 {
237 struct scene_obj_txt *txt;
238
239 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
240 if (!txt)
241 return log_msg_ret("find", -ENOENT);
242 txt->gen.font_name = font_name;
243 txt->gen.font_size = font_size;
244
245 return 0;
246 }
247
scene_obj_set_pos(struct scene * scn,uint id,int x,int y)248 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
249 {
250 struct scene_obj *obj;
251 int w, h;
252
253 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
254 if (!obj)
255 return log_msg_ret("find", -ENOENT);
256 w = obj->bbox.x1 - obj->bbox.x0;
257 h = obj->bbox.y1 - obj->bbox.y0;
258 obj->bbox.x0 = x;
259 obj->bbox.y0 = y;
260 obj->bbox.x1 = obj->bbox.x0 + w;
261 obj->bbox.y1 = obj->bbox.y0 + h;
262
263 return 0;
264 }
265
scene_obj_set_size(struct scene * scn,uint id,int w,int h)266 int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
267 {
268 struct scene_obj *obj;
269
270 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
271 if (!obj)
272 return log_msg_ret("find", -ENOENT);
273 obj->bbox.x1 = obj->bbox.x0 + w;
274 obj->bbox.y1 = obj->bbox.y0 + h;
275 obj->flags |= SCENEOF_SIZE_VALID;
276
277 return 0;
278 }
279
scene_obj_set_width(struct scene * scn,uint id,int w)280 int scene_obj_set_width(struct scene *scn, uint id, int w)
281 {
282 struct scene_obj *obj;
283
284 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
285 if (!obj)
286 return log_msg_ret("find", -ENOENT);
287 obj->bbox.x1 = obj->bbox.x0 + w;
288
289 return 0;
290 }
291
scene_obj_set_bbox(struct scene * scn,uint id,int x0,int y0,int x1,int y1)292 int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1,
293 int y1)
294 {
295 struct scene_obj *obj;
296
297 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
298 if (!obj)
299 return log_msg_ret("find", -ENOENT);
300 obj->bbox.x0 = x0;
301 obj->bbox.y0 = y0;
302 obj->bbox.x1 = x1;
303 obj->bbox.y1 = y1;
304 obj->flags |= SCENEOF_SIZE_VALID;
305
306 return 0;
307 }
308
scene_obj_set_halign(struct scene * scn,uint id,enum scene_obj_align aln)309 int scene_obj_set_halign(struct scene *scn, uint id, enum scene_obj_align aln)
310 {
311 struct scene_obj *obj;
312
313 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
314 if (!obj)
315 return log_msg_ret("osh", -ENOENT);
316 obj->horiz = aln;
317
318 return 0;
319 }
320
scene_obj_set_valign(struct scene * scn,uint id,enum scene_obj_align aln)321 int scene_obj_set_valign(struct scene *scn, uint id, enum scene_obj_align aln)
322 {
323 struct scene_obj *obj;
324
325 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
326 if (!obj)
327 return log_msg_ret("osv", -ENOENT);
328 obj->vert = aln;
329
330 return 0;
331 }
332
scene_obj_set_hide(struct scene * scn,uint id,bool hide)333 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
334 {
335 int ret;
336
337 ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
338 hide ? SCENEOF_HIDE : 0);
339 if (ret)
340 return log_msg_ret("flg", ret);
341
342 return 0;
343 }
344
scene_obj_flag_clrset(struct scene * scn,uint id,uint clr,uint set)345 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
346 {
347 struct scene_obj *obj;
348
349 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
350 if (!obj)
351 return log_msg_ret("find", -ENOENT);
352 obj->flags &= ~clr;
353 obj->flags |= set;
354
355 return 0;
356 }
357
handle_alignment(enum scene_obj_align horiz,enum scene_obj_align vert,struct scene_obj_bbox * bbox,struct scene_obj_dims * dims,int xsize,int ysize,struct scene_obj_offset * offset)358 static void handle_alignment(enum scene_obj_align horiz,
359 enum scene_obj_align vert,
360 struct scene_obj_bbox *bbox,
361 struct scene_obj_dims *dims,
362 int xsize, int ysize,
363 struct scene_obj_offset *offset)
364 {
365 int width, height;
366
367 if (bbox->x1 == SCENEOB_DISPLAY_MAX)
368 bbox->x1 = xsize ?: 1280;
369 if (bbox->y1 == SCENEOB_DISPLAY_MAX)
370 bbox->y1 = ysize ?: 1024;
371
372 width = bbox->x1 - bbox->x0;
373 height = bbox->y1 - bbox->y0;
374
375 switch (horiz) {
376 case SCENEOA_CENTRE:
377 offset->xofs = (width - dims->x) / 2;
378 break;
379 case SCENEOA_RIGHT:
380 offset->xofs = width - dims->x;
381 break;
382 case SCENEOA_LEFT:
383 offset->xofs = 0;
384 break;
385 }
386
387 switch (vert) {
388 case SCENEOA_CENTRE:
389 offset->yofs = (height - dims->y) / 2;
390 break;
391 case SCENEOA_BOTTOM:
392 offset->yofs = height - dims->y;
393 break;
394 case SCENEOA_TOP:
395 default:
396 offset->yofs = 0;
397 break;
398 }
399 }
400
scene_obj_get_hw(struct scene * scn,uint id,int * widthp)401 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
402 {
403 struct scene_obj *obj;
404
405 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
406 if (!obj)
407 return log_msg_ret("find", -ENOENT);
408
409 switch (obj->type) {
410 case SCENEOBJT_NONE:
411 case SCENEOBJT_MENU:
412 case SCENEOBJT_TEXTLINE:
413 case SCENEOBJT_BOX:
414 break;
415 case SCENEOBJT_IMAGE: {
416 struct scene_obj_img *img = (struct scene_obj_img *)obj;
417 ulong width, height;
418 uint bpix;
419
420 video_bmp_get_info(img->data, &width, &height, &bpix);
421 if (widthp)
422 *widthp = width;
423 return height;
424 }
425 case SCENEOBJT_TEXT:
426 case SCENEOBJT_TEXTEDIT: {
427 struct scene_txt_generic *gen;
428 struct expo *exp = scn->expo;
429 struct vidconsole_bbox bbox;
430 int len, ret, limit;
431 const char *str;
432
433 if (obj->type == SCENEOBJT_TEXT)
434 gen = &((struct scene_obj_txt *)obj)->gen;
435 else
436 gen = &((struct scene_obj_txtedit *)obj)->gen;
437
438 str = expo_get_str(exp, gen->str_id);
439 if (!str)
440 return log_msg_ret("str", -ENOENT);
441 len = strlen(str);
442
443 /* if there is no console, make it up */
444 if (!exp->cons) {
445 if (widthp)
446 *widthp = 8 * len;
447 return 16;
448 }
449
450 limit = obj->flags & SCENEOF_SIZE_VALID ?
451 obj->bbox.x1 - obj->bbox.x0 : -1;
452
453 ret = vidconsole_measure(scn->expo->cons, gen->font_name,
454 gen->font_size, str, limit, &bbox,
455 &gen->lines);
456 if (ret)
457 return log_msg_ret("mea", ret);
458 if (widthp)
459 *widthp = bbox.x1;
460
461 return bbox.y1;
462 }
463 }
464
465 return 0;
466 }
467
468 /**
469 * scene_render_background() - Render the background for an object
470 *
471 * @obj: Object to render
472 * @box_only: true to show a box around the object, but keep the normal
473 * background colour inside
474 * @cur_item: true to render the background only for the current menu item
475 */
scene_render_background(struct scene_obj * obj,bool box_only,bool cur_item)476 static void scene_render_background(struct scene_obj *obj, bool box_only,
477 bool cur_item)
478 {
479 struct vidconsole_bbox bbox[SCENEBB_count], *sel;
480 struct expo *exp = obj->scene->expo;
481 const struct expo_theme *theme = &exp->theme;
482 struct udevice *dev = exp->display;
483 struct video_priv *vid_priv;
484 struct udevice *cons = exp->cons;
485 struct vidconsole_colour old;
486 enum colour_idx fore, back;
487 uint inset = theme->menu_inset;
488
489 vid_priv = dev_get_uclass_priv(dev);
490 /* draw a background for the object */
491 if (vid_priv->white_on_black) {
492 fore = VID_DARK_GREY;
493 back = VID_WHITE;
494 } else {
495 fore = VID_LIGHT_GRAY;
496 back = VID_BLACK;
497 }
498
499 /* see if this object wants to render a background */
500 if (scene_obj_calc_bbox(obj, bbox))
501 return;
502
503 sel = cur_item ? &bbox[SCENEBB_curitem] : &bbox[SCENEBB_label];
504 if (!sel->valid)
505 return;
506
507 vidconsole_push_colour(cons, fore, back, &old);
508 video_fill_part(dev, sel->x0 - inset, sel->y0 - inset,
509 sel->x1 + inset, sel->y1 + inset,
510 vid_priv->colour_fg);
511 vidconsole_pop_colour(cons, &old);
512 if (box_only) {
513 video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1,
514 vid_priv->colour_bg);
515 }
516 }
517
scene_txt_render(struct expo * exp,struct udevice * dev,struct udevice * cons,struct scene_obj * obj,struct scene_txt_generic * gen,int x,int y,int menu_inset)518 static int scene_txt_render(struct expo *exp, struct udevice *dev,
519 struct udevice *cons, struct scene_obj *obj,
520 struct scene_txt_generic *gen, int x, int y,
521 int menu_inset)
522 {
523 const struct vidconsole_mline *mline, *last;
524 struct video_priv *vid_priv;
525 struct vidconsole_colour old;
526 enum colour_idx fore, back;
527 struct scene_obj_dims dims;
528 struct scene_obj_bbox bbox;
529 const char *str;
530 int ret;
531
532 if (!cons)
533 return -ENOTSUPP;
534
535 if (gen->font_name || gen->font_size) {
536 ret = vidconsole_select_font(cons, gen->font_name,
537 gen->font_size);
538 } else {
539 ret = vidconsole_select_font(cons, NULL, 0);
540 }
541 if (ret && ret != -ENOSYS)
542 return log_msg_ret("font", ret);
543 str = expo_get_str(exp, gen->str_id);
544 if (!str)
545 return 0;
546
547 vid_priv = dev_get_uclass_priv(dev);
548 if (vid_priv->white_on_black) {
549 fore = VID_BLACK;
550 back = VID_WHITE;
551 } else {
552 fore = VID_LIGHT_GRAY;
553 back = VID_BLACK;
554 }
555
556 if (obj->flags & SCENEOF_POINT) {
557 int inset;
558
559 inset = exp->popup ? menu_inset : 0;
560 vidconsole_push_colour(cons, fore, back, &old);
561 video_fill_part(dev, x - inset, y,
562 obj->bbox.x1, obj->bbox.y1,
563 vid_priv->colour_bg);
564 }
565
566 mline = alist_get(&gen->lines, 0, typeof(*mline));
567 last = alist_get(&gen->lines, gen->lines.count - 1, typeof(*mline));
568 if (mline)
569 dims.y = last->bbox.y1 - mline->bbox.y0;
570 bbox.y0 = obj->bbox.y0;
571 bbox.y1 = obj->bbox.y1;
572
573 if (!mline) {
574 vidconsole_set_cursor_pos(cons, x, y);
575 vidconsole_put_string(cons, str);
576 }
577
578 alist_for_each(mline, &gen->lines) {
579 struct scene_obj_offset offset;
580
581 bbox.x0 = obj->bbox.x0;
582 bbox.x1 = obj->bbox.x1;
583 dims.x = mline->bbox.x1 - mline->bbox.x0;
584 handle_alignment(obj->horiz, obj->vert, &bbox, &dims,
585 obj->bbox.x1 - obj->bbox.x0,
586 obj->bbox.y1 - obj->bbox.y0, &offset);
587
588 x = obj->bbox.x0 + offset.xofs;
589 y = obj->bbox.y0 + offset.yofs + mline->bbox.y0;
590 if (y > bbox.y1)
591 break; /* clip this line and any following */
592 vidconsole_set_cursor_pos(cons, x, y);
593 vidconsole_put_stringn(cons, str + mline->start, mline->len);
594 }
595 if (obj->flags & SCENEOF_POINT)
596 vidconsole_pop_colour(cons, &old);
597
598 return 0;
599 }
600
601 /**
602 * scene_obj_render() - Render an object
603 *
604 * @obj: Object to render
605 * @text_mode: true to use text mode
606 * Return: 0 if OK, -ve on error
607 */
scene_obj_render(struct scene_obj * obj,bool text_mode)608 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
609 {
610 struct scene *scn = obj->scene;
611 struct expo *exp = scn->expo;
612 const struct expo_theme *theme = &exp->theme;
613 struct udevice *dev = exp->display;
614 struct udevice *cons = text_mode ? NULL : exp->cons;
615 struct video_priv *vid_priv;
616 int x, y, ret;
617
618 y = obj->bbox.y0;
619 x = obj->bbox.x0 + obj->ofs.xofs;
620 vid_priv = dev_get_uclass_priv(dev);
621
622 switch (obj->type) {
623 case SCENEOBJT_NONE:
624 break;
625 case SCENEOBJT_IMAGE: {
626 struct scene_obj_img *img = (struct scene_obj_img *)obj;
627
628 if (!cons)
629 return -ENOTSUPP;
630 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
631 true);
632 if (ret < 0)
633 return log_msg_ret("img", ret);
634 break;
635 }
636 case SCENEOBJT_TEXT: {
637 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
638
639 ret = scene_txt_render(exp, dev, cons, obj, &txt->gen, x, y,
640 theme->menu_inset);
641 break;
642 }
643 case SCENEOBJT_MENU: {
644 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
645
646 if (exp->popup) {
647 if (obj->flags & SCENEOF_OPEN) {
648 if (!cons)
649 return -ENOTSUPP;
650
651 /* draw a background behind the menu items */
652 scene_render_background(obj, false, false);
653 }
654 } else if (exp->show_highlight) {
655 /* do nothing */
656 }
657
658 /*
659 * With a vidconsole, the text and item pointer are rendered as
660 * normal objects so we don't need to do anything here. The menu
661 * simply controls where they are positioned.
662 */
663 if (cons)
664 return -ENOTSUPP;
665
666 ret = scene_menu_display(menu);
667 if (ret < 0)
668 return log_msg_ret("img", ret);
669
670 break;
671 }
672 case SCENEOBJT_TEXTLINE:
673 if (obj->flags & SCENEOF_OPEN)
674 scene_render_background(obj, true, false);
675 break;
676 case SCENEOBJT_BOX: {
677 struct scene_obj_box *box = (struct scene_obj_box *)obj;
678
679 video_draw_box(dev, obj->bbox.x0, obj->bbox.y0, obj->bbox.x1,
680 obj->bbox.y1, box->width, vid_priv->colour_fg);
681 break;
682 }
683 case SCENEOBJT_TEXTEDIT: {
684 struct scene_obj_txtedit *ted = (struct scene_obj_txtedit *)obj;
685
686 ret = scene_txt_render(exp, dev, cons, obj, &ted->gen, x, y,
687 theme->menu_inset);
688 break;
689 }
690 }
691
692 return 0;
693 }
694
scene_calc_arrange(struct scene * scn,struct expo_arrange_info * arr)695 int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
696 {
697 struct scene_obj *obj;
698
699 arr->label_width = 0;
700 list_for_each_entry(obj, &scn->obj_head, sibling) {
701 uint label_id = 0;
702 int width;
703
704 switch (obj->type) {
705 case SCENEOBJT_NONE:
706 case SCENEOBJT_IMAGE:
707 case SCENEOBJT_TEXT:
708 case SCENEOBJT_BOX:
709 case SCENEOBJT_TEXTEDIT:
710 break;
711 case SCENEOBJT_MENU: {
712 struct scene_obj_menu *menu;
713
714 menu = (struct scene_obj_menu *)obj,
715 label_id = menu->title_id;
716 break;
717 }
718 case SCENEOBJT_TEXTLINE: {
719 struct scene_obj_textline *tline;
720
721 tline = (struct scene_obj_textline *)obj,
722 label_id = tline->label_id;
723 break;
724 }
725 }
726
727 if (label_id) {
728 int ret;
729
730 ret = scene_obj_get_hw(scn, label_id, &width);
731 if (ret < 0)
732 return log_msg_ret("hei", ret);
733 arr->label_width = max(arr->label_width, width);
734 }
735 }
736
737 return 0;
738 }
739
scene_arrange(struct scene * scn)740 int scene_arrange(struct scene *scn)
741 {
742 struct expo_arrange_info arr;
743 int xsize = 0, ysize = 0;
744 struct scene_obj *obj;
745 struct udevice *dev;
746 int ret;
747
748 dev = scn->expo->display;
749 if (dev) {
750 struct video_priv *priv = dev_get_uclass_priv(dev);
751
752 xsize = priv->xsize;
753 ysize = priv->ysize;
754 }
755
756 ret = scene_calc_arrange(scn, &arr);
757 if (ret < 0)
758 return log_msg_ret("arr", ret);
759
760 list_for_each_entry(obj, &scn->obj_head, sibling) {
761 handle_alignment(obj->horiz, obj->vert, &obj->bbox, &obj->dims,
762 xsize, ysize, &obj->ofs);
763
764 switch (obj->type) {
765 case SCENEOBJT_NONE:
766 case SCENEOBJT_IMAGE:
767 case SCENEOBJT_TEXT:
768 case SCENEOBJT_BOX:
769 case SCENEOBJT_TEXTEDIT:
770 break;
771 case SCENEOBJT_MENU: {
772 struct scene_obj_menu *menu;
773
774 menu = (struct scene_obj_menu *)obj,
775 ret = scene_menu_arrange(scn, &arr, menu);
776 if (ret)
777 return log_msg_ret("arr", ret);
778 break;
779 }
780 case SCENEOBJT_TEXTLINE: {
781 struct scene_obj_textline *tline;
782
783 tline = (struct scene_obj_textline *)obj,
784 ret = scene_textline_arrange(scn, &arr, tline);
785 if (ret)
786 return log_msg_ret("arr", ret);
787 break;
788 }
789 }
790 }
791
792 return 0;
793 }
794
scene_render_deps(struct scene * scn,uint id)795 int scene_render_deps(struct scene *scn, uint id)
796 {
797 struct scene_obj *obj;
798 int ret;
799
800 if (!id)
801 return 0;
802 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
803 if (!obj)
804 return log_msg_ret("obj", -ENOENT);
805
806 if (!(obj->flags & SCENEOF_HIDE)) {
807 ret = scene_obj_render(obj, false);
808 if (ret && ret != -ENOTSUPP)
809 return log_msg_ret("ren", ret);
810
811 switch (obj->type) {
812 case SCENEOBJT_NONE:
813 case SCENEOBJT_IMAGE:
814 case SCENEOBJT_TEXT:
815 case SCENEOBJT_BOX:
816 case SCENEOBJT_TEXTEDIT:
817 break;
818 case SCENEOBJT_MENU:
819 scene_menu_render_deps(scn,
820 (struct scene_obj_menu *)obj);
821 break;
822 case SCENEOBJT_TEXTLINE:
823 scene_textline_render_deps(scn,
824 (struct scene_obj_textline *)obj);
825 break;
826 }
827 }
828
829 return 0;
830 }
831
scene_render(struct scene * scn)832 int scene_render(struct scene *scn)
833 {
834 struct expo *exp = scn->expo;
835 struct scene_obj *obj;
836 int ret;
837
838 list_for_each_entry(obj, &scn->obj_head, sibling) {
839 if (!(obj->flags & SCENEOF_HIDE)) {
840 ret = scene_obj_render(obj, exp->text_mode);
841 if (ret && ret != -ENOTSUPP)
842 return log_msg_ret("ren", ret);
843 }
844 }
845
846 /* render any highlighted object on top of the others */
847 if (scn->highlight_id && !exp->text_mode) {
848 ret = scene_render_deps(scn, scn->highlight_id);
849 if (ret && ret != -ENOTSUPP)
850 return log_msg_ret("dep", ret);
851 }
852
853 return 0;
854 }
855
856 /**
857 * send_key_obj() - Handle a keypress for moving between objects
858 *
859 * @scn: Scene to receive the key
860 * @key: Key to send (KEYCODE_UP)
861 * @event: Returns resulting event from this keypress
862 * Returns: 0 if OK, -ve on error
863 */
send_key_obj(struct scene * scn,struct scene_obj * obj,int key,struct expo_action * event)864 static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key,
865 struct expo_action *event)
866 {
867 switch (key) {
868 case BKEY_UP:
869 while (obj != list_first_entry(&scn->obj_head, struct scene_obj,
870 sibling)) {
871 obj = list_entry(obj->sibling.prev,
872 struct scene_obj, sibling);
873 if (scene_obj_can_highlight(obj)) {
874 event->type = EXPOACT_POINT_OBJ;
875 event->select.id = obj->id;
876 log_debug("up to obj %d\n", event->select.id);
877 break;
878 }
879 }
880 break;
881 case BKEY_DOWN:
882 while (!list_is_last(&obj->sibling, &scn->obj_head)) {
883 obj = list_entry(obj->sibling.next, struct scene_obj,
884 sibling);
885 if (scene_obj_can_highlight(obj)) {
886 event->type = EXPOACT_POINT_OBJ;
887 event->select.id = obj->id;
888 log_debug("down to obj %d\n", event->select.id);
889 break;
890 }
891 }
892 break;
893 case BKEY_SELECT:
894 if (scene_obj_can_highlight(obj)) {
895 event->type = EXPOACT_OPEN;
896 event->select.id = obj->id;
897 log_debug("open obj %d\n", event->select.id);
898 }
899 break;
900 case BKEY_QUIT:
901 event->type = EXPOACT_QUIT;
902 log_debug("obj quit\n");
903 break;
904 }
905 }
906
scene_send_key(struct scene * scn,int key,struct expo_action * event)907 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
908 {
909 struct scene_obj *obj;
910 int ret;
911
912 event->type = EXPOACT_NONE;
913
914 /*
915 * In 'popup' mode, arrow keys move betwen objects, unless a menu is
916 * opened
917 */
918 if (scn->expo->popup) {
919 obj = NULL;
920 if (scn->highlight_id) {
921 obj = scene_obj_find(scn, scn->highlight_id,
922 SCENEOBJT_NONE);
923 }
924 if (!obj)
925 return 0;
926
927 if (!(obj->flags & SCENEOF_OPEN)) {
928 send_key_obj(scn, obj, key, event);
929 return 0;
930 }
931
932 switch (obj->type) {
933 case SCENEOBJT_NONE:
934 case SCENEOBJT_IMAGE:
935 case SCENEOBJT_TEXT:
936 case SCENEOBJT_BOX:
937 break;
938 case SCENEOBJT_MENU: {
939 struct scene_obj_menu *menu;
940
941 menu = (struct scene_obj_menu *)obj,
942 ret = scene_menu_send_key(scn, menu, key, event);
943 if (ret)
944 return log_msg_ret("key", ret);
945 break;
946 }
947 case SCENEOBJT_TEXTLINE: {
948 struct scene_obj_textline *tline;
949
950 tline = (struct scene_obj_textline *)obj,
951 ret = scene_textline_send_key(scn, tline, key, event);
952 if (ret)
953 return log_msg_ret("key", ret);
954 break;
955 }
956 case SCENEOBJT_TEXTEDIT:
957 /* TODO(sjg@chromium.org): Implement this */
958 break;
959 }
960 return 0;
961 }
962
963 list_for_each_entry(obj, &scn->obj_head, sibling) {
964 if (obj->type == SCENEOBJT_MENU) {
965 struct scene_obj_menu *menu;
966
967 menu = (struct scene_obj_menu *)obj,
968 ret = scene_menu_send_key(scn, menu, key, event);
969 if (ret)
970 return log_msg_ret("key", ret);
971 break;
972 }
973 }
974
975 return 0;
976 }
977
scene_obj_calc_bbox(struct scene_obj * obj,struct vidconsole_bbox bbox[])978 int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[])
979 {
980 switch (obj->type) {
981 case SCENEOBJT_NONE:
982 case SCENEOBJT_IMAGE:
983 case SCENEOBJT_TEXT:
984 case SCENEOBJT_BOX:
985 case SCENEOBJT_TEXTEDIT:
986 return -ENOSYS;
987 case SCENEOBJT_MENU: {
988 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
989
990 scene_menu_calc_bbox(menu, bbox);
991 break;
992 }
993 case SCENEOBJT_TEXTLINE: {
994 struct scene_obj_textline *tline;
995
996 tline = (struct scene_obj_textline *)obj;
997 scene_textline_calc_bbox(tline, &bbox[SCENEBB_all],
998 &bbox[SCENEBB_label]);
999 break;
1000 }
1001 }
1002
1003 return 0;
1004 }
1005
scene_calc_dims(struct scene * scn,bool do_menus)1006 int scene_calc_dims(struct scene *scn, bool do_menus)
1007 {
1008 struct scene_obj *obj;
1009 int ret;
1010
1011 list_for_each_entry(obj, &scn->obj_head, sibling) {
1012 switch (obj->type) {
1013 case SCENEOBJT_NONE:
1014 case SCENEOBJT_TEXT:
1015 case SCENEOBJT_BOX:
1016 case SCENEOBJT_TEXTEDIT:
1017 case SCENEOBJT_IMAGE: {
1018 int width;
1019
1020 if (!do_menus) {
1021 ret = scene_obj_get_hw(scn, obj->id, &width);
1022 if (ret < 0)
1023 return log_msg_ret("get", ret);
1024 obj->dims.x = width;
1025 obj->dims.y = ret;
1026 if (!(obj->flags & SCENEOF_SIZE_VALID)) {
1027 obj->bbox.x1 = obj->bbox.x0 + width;
1028 obj->bbox.y1 = obj->bbox.y0 + ret;
1029 obj->flags |= SCENEOF_SIZE_VALID;
1030 }
1031 }
1032 break;
1033 }
1034 case SCENEOBJT_MENU: {
1035 struct scene_obj_menu *menu;
1036
1037 if (do_menus) {
1038 menu = (struct scene_obj_menu *)obj;
1039
1040 ret = scene_menu_calc_dims(menu);
1041 if (ret)
1042 return log_msg_ret("men", ret);
1043 }
1044 break;
1045 }
1046 case SCENEOBJT_TEXTLINE: {
1047 struct scene_obj_textline *tline;
1048
1049 tline = (struct scene_obj_textline *)obj;
1050 ret = scene_textline_calc_dims(tline);
1051 if (ret)
1052 return log_msg_ret("men", ret);
1053
1054 break;
1055 }
1056 }
1057 }
1058
1059 return 0;
1060 }
1061
scene_apply_theme(struct scene * scn,struct expo_theme * theme)1062 int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
1063 {
1064 struct scene_obj *obj;
1065 int ret;
1066
1067 /* Avoid error-checking optional items */
1068 scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
1069
1070 list_for_each_entry(obj, &scn->obj_head, sibling) {
1071 switch (obj->type) {
1072 case SCENEOBJT_NONE:
1073 case SCENEOBJT_IMAGE:
1074 case SCENEOBJT_MENU:
1075 case SCENEOBJT_BOX:
1076 case SCENEOBJT_TEXTLINE:
1077 break;
1078 case SCENEOBJT_TEXTEDIT:
1079 scene_txted_set_font(scn, obj->id, NULL,
1080 theme->font_size);
1081 break;
1082 case SCENEOBJT_TEXT:
1083 scene_txt_set_font(scn, obj->id, NULL,
1084 theme->font_size);
1085 break;
1086 }
1087 }
1088
1089 ret = scene_arrange(scn);
1090 if (ret)
1091 return log_msg_ret("arr", ret);
1092
1093 return 0;
1094 }
1095
scene_set_highlight_id(struct scene * scn,uint id)1096 void scene_set_highlight_id(struct scene *scn, uint id)
1097 {
1098 scn->highlight_id = id;
1099 }
1100
scene_highlight_first(struct scene * scn)1101 void scene_highlight_first(struct scene *scn)
1102 {
1103 struct scene_obj *obj;
1104
1105 list_for_each_entry(obj, &scn->obj_head, sibling) {
1106 if (scene_obj_can_highlight(obj)) {
1107 scene_set_highlight_id(scn, obj->id);
1108 return;
1109 }
1110 }
1111 }
1112
scene_obj_open(struct scene * scn,struct scene_obj * obj)1113 static int scene_obj_open(struct scene *scn, struct scene_obj *obj)
1114 {
1115 int ret;
1116
1117 switch (obj->type) {
1118 case SCENEOBJT_NONE:
1119 case SCENEOBJT_IMAGE:
1120 case SCENEOBJT_MENU:
1121 case SCENEOBJT_TEXT:
1122 case SCENEOBJT_BOX:
1123 case SCENEOBJT_TEXTEDIT:
1124 break;
1125 case SCENEOBJT_TEXTLINE:
1126 ret = scene_textline_open(scn,
1127 (struct scene_obj_textline *)obj);
1128 if (ret)
1129 return log_msg_ret("op", ret);
1130 break;
1131 }
1132
1133 return 0;
1134 }
1135
scene_set_open(struct scene * scn,uint id,bool open)1136 int scene_set_open(struct scene *scn, uint id, bool open)
1137 {
1138 struct scene_obj *obj;
1139 int ret;
1140
1141 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1142 if (!obj)
1143 return log_msg_ret("find", -ENOENT);
1144
1145 if (open) {
1146 ret = scene_obj_open(scn, obj);
1147 if (ret)
1148 return log_msg_ret("op", ret);
1149 }
1150
1151 ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
1152 open ? SCENEOF_OPEN : 0);
1153 if (ret)
1154 return log_msg_ret("flg", ret);
1155
1156 return 0;
1157 }
1158
scene_iter_objs(struct scene * scn,expo_scene_obj_iterator iter,void * priv)1159 int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter,
1160 void *priv)
1161 {
1162 struct scene_obj *obj;
1163
1164 list_for_each_entry(obj, &scn->obj_head, sibling) {
1165 int ret;
1166
1167 ret = iter(obj, priv);
1168 if (ret)
1169 return log_msg_ret("itr", ret);
1170 }
1171
1172 return 0;
1173 }
1174
scene_bbox_join(const struct vidconsole_bbox * src,int inset,struct vidconsole_bbox * dst)1175 int scene_bbox_join(const struct vidconsole_bbox *src, int inset,
1176 struct vidconsole_bbox *dst)
1177 {
1178 if (dst->valid) {
1179 dst->x0 = min(dst->x0, src->x0 - inset);
1180 dst->y0 = min(dst->y0, src->y0);
1181 dst->x1 = max(dst->x1, src->x1 + inset);
1182 dst->y1 = max(dst->y1, src->y1);
1183 } else {
1184 dst->x0 = src->x0 - inset;
1185 dst->y0 = src->y0;
1186 dst->x1 = src->x1 + inset;
1187 dst->y1 = src->y1;
1188 dst->valid = true;
1189 }
1190
1191 return 0;
1192 }
1193
scene_bbox_union(struct scene * scn,uint id,int inset,struct vidconsole_bbox * bbox)1194 int scene_bbox_union(struct scene *scn, uint id, int inset,
1195 struct vidconsole_bbox *bbox)
1196 {
1197 struct scene_obj *obj;
1198 struct vidconsole_bbox local;
1199
1200 if (!id)
1201 return 0;
1202 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
1203 if (!obj)
1204 return log_msg_ret("obj", -ENOENT);
1205 local.x0 = obj->bbox.x0;
1206 local.y0 = obj->bbox.y0;
1207 local.x1 = obj->bbox.x1;
1208 local.y1 = obj->bbox.y1;
1209 local.valid = true;
1210 scene_bbox_join(&local, inset, bbox);
1211
1212 return 0;
1213 }
1214