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