Commit | Line | Data |
---|---|---|
5e2607ae SG |
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 | ||
c98cb512 SG |
9 | #define LOG_CATEGORY LOGC_EXPO |
10 | ||
5e2607ae SG |
11 | #include <dm.h> |
12 | #include <expo.h> | |
13 | #include <malloc.h> | |
14 | #include <mapmem.h> | |
4e64beeb | 15 | #include <menu.h> |
5e2607ae SG |
16 | #include <video.h> |
17 | #include <video_console.h> | |
18 | #include <linux/input.h> | |
19 | #include "scene_internal.h" | |
20 | ||
5e2607ae SG |
21 | int 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 | ||
93f99b35 SG |
34 | 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 | ||
5e2607ae SG |
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 | ||
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 | ||
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 | ||
93f99b35 SG |
67 | abuf_uninit(&scn->entry_save); |
68 | abuf_uninit(&scn->buf); | |
5e2607ae | 69 | free(scn->name); |
5e2607ae SG |
70 | free(scn); |
71 | } | |
72 | ||
5e2607ae SG |
73 | int scene_obj_count(struct scene *scn) |
74 | { | |
be222ac0 | 75 | return list_count_nodes(&scn->obj_head); |
5e2607ae SG |
76 | } |
77 | ||
633b3dc7 | 78 | void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type) |
5e2607ae SG |
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 | ||
a0874dc4 SG |
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 | ||
5e2607ae SG |
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 | ||
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) | |
9767de7b | 136 | return log_msg_ret("obj", ret); |
5e2607ae SG |
137 | |
138 | img->data = data; | |
139 | ||
140 | if (imgp) | |
141 | *imgp = img; | |
142 | ||
143 | return img->obj.id; | |
144 | } | |
145 | ||
146 | int 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) | |
9767de7b | 156 | return log_msg_ret("obj", ret); |
5e2607ae SG |
157 | |
158 | txt->str_id = str_id; | |
159 | ||
160 | if (txtp) | |
161 | *txtp = txt; | |
162 | ||
163 | return txt->obj.id; | |
164 | } | |
165 | ||
166 | int 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); | |
408011c2 | 175 | if (str_id && ret != str_id) |
5e2607ae | 176 | return log_msg_ret("id", -EEXIST); |
408011c2 | 177 | str_id = ret; |
5e2607ae SG |
178 | |
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) | |
9767de7b | 183 | return log_msg_ret("obj", ret); |
5e2607ae SG |
184 | |
185 | txt->str_id = str_id; | |
186 | ||
187 | if (txtp) | |
188 | *txtp = txt; | |
189 | ||
190 | return txt->obj.id; | |
191 | } | |
192 | ||
193 | int 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 | ||
207 | int 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); | |
ae45d6cf SG |
214 | obj->dim.x = x; |
215 | obj->dim.y = y; | |
5e2607ae SG |
216 | |
217 | return 0; | |
218 | } | |
219 | ||
699b0acb SG |
220 | int 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 | ||
5e2607ae | 233 | int scene_obj_set_hide(struct scene *scn, uint id, bool hide) |
ce72c9ec SG |
234 | { |
235 | 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 | ||
245 | int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set) | |
5e2607ae SG |
246 | { |
247 | struct scene_obj *obj; | |
248 | ||
249 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); | |
250 | if (!obj) | |
251 | return log_msg_ret("find", -ENOENT); | |
ce72c9ec SG |
252 | obj->flags &= ~clr; |
253 | obj->flags |= set; | |
5e2607ae SG |
254 | |
255 | return 0; | |
256 | } | |
257 | ||
258 | int 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: | |
c4fea34f | 269 | case SCENEOBJT_TEXTLINE: |
5e2607ae SG |
270 | 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; | |
50f02037 SG |
284 | struct vidconsole_bbox bbox; |
285 | const char *str; | |
286 | int len, ret; | |
287 | ||
288 | 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); | |
5e2607ae | 304 | if (widthp) |
50f02037 SG |
305 | *widthp = bbox.x1; |
306 | ||
307 | return bbox.y1; | |
5e2607ae SG |
308 | } |
309 | } | |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
94598d5b SG |
314 | /** |
315 | * scene_render_background() - Render the background for an object | |
316 | * | |
317 | * @obj: Object to render | |
c4fea34f SG |
318 | * @box_only: true to show a box around the object, but keep the normal |
319 | * background colour inside | |
94598d5b | 320 | */ |
c4fea34f | 321 | static void scene_render_background(struct scene_obj *obj, bool box_only) |
94598d5b SG |
322 | { |
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)) { | |
52c19173 | 335 | fore = VID_DARK_GREY; |
94598d5b SG |
336 | 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); | |
c4fea34f SG |
352 | 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 | } | |
94598d5b SG |
357 | } |
358 | ||
5e2607ae SG |
359 | /** |
360 | * scene_obj_render() - Render an object | |
361 | * | |
362 | */ | |
363 | static int scene_obj_render(struct scene_obj *obj, bool text_mode) | |
364 | { | |
365 | struct scene *scn = obj->scene; | |
366 | struct expo *exp = scn->expo; | |
7230fdb3 | 367 | const struct expo_theme *theme = &exp->theme; |
42b18494 SG |
368 | struct udevice *dev = exp->display; |
369 | struct udevice *cons = text_mode ? NULL : exp->cons; | |
5e2607ae SG |
370 | int x, y, ret; |
371 | ||
ae45d6cf SG |
372 | x = obj->dim.x; |
373 | y = obj->dim.y; | |
5e2607ae SG |
374 | |
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); | |
5e2607ae | 405 | str = expo_get_str(exp, txt->str_id); |
756c9559 SG |
406 | 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); | |
7230fdb3 SG |
422 | video_fill_part(dev, x - theme->menu_inset, y, |
423 | x + obj->dim.w, | |
424 | y + obj->dim.h, | |
756c9559 SG |
425 | vid_priv->colour_bg); |
426 | } | |
427 | vidconsole_set_cursor_pos(cons, x, y); | |
5e2607ae | 428 | vidconsole_put_string(cons, str); |
756c9559 SG |
429 | if (obj->flags & SCENEOF_POINT) |
430 | vidconsole_pop_colour(cons, &old); | |
431 | } | |
5e2607ae SG |
432 | break; |
433 | } | |
434 | case SCENEOBJT_MENU: { | |
435 | struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; | |
756c9559 SG |
436 | |
437 | if (exp->popup && (obj->flags & SCENEOF_OPEN)) { | |
438 | if (!cons) | |
439 | return -ENOTSUPP; | |
440 | ||
441 | /* draw a background behind the menu items */ | |
c4fea34f | 442 | scene_render_background(obj, false); |
756c9559 | 443 | } |
5e2607ae SG |
444 | /* |
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 | } | |
c4fea34f SG |
458 | case SCENEOBJT_TEXTLINE: |
459 | if (obj->flags & SCENEOF_OPEN) | |
460 | scene_render_background(obj, true); | |
461 | break; | |
5e2607ae SG |
462 | } |
463 | ||
464 | return 0; | |
465 | } | |
466 | ||
54eca1d3 SG |
467 | int 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 | ||
5e2607ae SG |
510 | int scene_arrange(struct scene *scn) |
511 | { | |
54eca1d3 | 512 | struct expo_arrange_info arr; |
5e2607ae SG |
513 | struct scene_obj *obj; |
514 | int ret; | |
515 | ||
54eca1d3 SG |
516 | ret = scene_calc_arrange(scn, &arr); |
517 | if (ret < 0) | |
518 | return log_msg_ret("arr", ret); | |
519 | ||
5e2607ae | 520 | list_for_each_entry(obj, &scn->obj_head, sibling) { |
fd6073ac SG |
521 | switch (obj->type) { |
522 | case SCENEOBJT_NONE: | |
523 | case SCENEOBJT_IMAGE: | |
524 | case SCENEOBJT_TEXT: | |
525 | break; | |
526 | case SCENEOBJT_MENU: { | |
5e2607ae SG |
527 | struct scene_obj_menu *menu; |
528 | ||
529 | menu = (struct scene_obj_menu *)obj, | |
54eca1d3 | 530 | ret = scene_menu_arrange(scn, &arr, menu); |
5e2607ae SG |
531 | if (ret) |
532 | return log_msg_ret("arr", ret); | |
fd6073ac SG |
533 | break; |
534 | } | |
c4fea34f SG |
535 | case SCENEOBJT_TEXTLINE: { |
536 | struct scene_obj_textline *tline; | |
537 | ||
538 | tline = (struct scene_obj_textline *)obj, | |
54eca1d3 | 539 | ret = scene_textline_arrange(scn, &arr, tline); |
c4fea34f SG |
540 | if (ret) |
541 | return log_msg_ret("arr", ret); | |
542 | break; | |
543 | } | |
5e2607ae SG |
544 | } |
545 | } | |
546 | ||
547 | return 0; | |
548 | } | |
549 | ||
4c87e073 SG |
550 | int 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 | ||
fd6073ac SG |
566 | switch (obj->type) { |
567 | case SCENEOBJT_NONE: | |
568 | case SCENEOBJT_IMAGE: | |
569 | case SCENEOBJT_TEXT: | |
570 | break; | |
571 | case SCENEOBJT_MENU: | |
4c87e073 SG |
572 | scene_menu_render_deps(scn, |
573 | (struct scene_obj_menu *)obj); | |
fd6073ac | 574 | break; |
c4fea34f SG |
575 | case SCENEOBJT_TEXTLINE: |
576 | scene_textline_render_deps(scn, | |
577 | (struct scene_obj_textline *)obj); | |
578 | break; | |
fd6073ac | 579 | } |
4c87e073 SG |
580 | } |
581 | ||
582 | return 0; | |
583 | } | |
584 | ||
5e2607ae SG |
585 | int 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) { | |
ce72c9ec | 592 | if (!(obj->flags & SCENEOF_HIDE)) { |
5e2607ae SG |
593 | ret = scene_obj_render(obj, exp->text_mode); |
594 | if (ret && ret != -ENOTSUPP) | |
595 | return log_msg_ret("ren", ret); | |
596 | } | |
597 | } | |
598 | ||
4c87e073 SG |
599 | /* 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 | ||
5e2607ae SG |
606 | return 0; |
607 | } | |
608 | ||
4e64beeb SG |
609 | /** |
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 | */ | |
617 | static 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); | |
d88edd2b | 626 | if (scene_obj_can_highlight(obj)) { |
4e64beeb SG |
627 | 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); | |
d88edd2b | 638 | if (scene_obj_can_highlight(obj)) { |
4e64beeb SG |
639 | 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: | |
d88edd2b | 647 | if (scene_obj_can_highlight(obj)) { |
4e64beeb SG |
648 | 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 | ||
5e2607ae SG |
660 | int scene_send_key(struct scene *scn, int key, struct expo_action *event) |
661 | { | |
662 | struct scene_obj *obj; | |
663 | int ret; | |
664 | ||
4e64beeb SG |
665 | 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 | ||
fd6073ac SG |
685 | 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 | } | |
c4fea34f SG |
699 | 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 | } | |
fd6073ac | 708 | } |
4e64beeb SG |
709 | return 0; |
710 | } | |
711 | ||
5e2607ae SG |
712 | 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); | |
5e2607ae SG |
720 | break; |
721 | } | |
722 | } | |
723 | ||
724 | return 0; | |
725 | } | |
699b0acb | 726 | |
8bc69b4b SG |
727 | int 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 | } | |
c4fea34f SG |
741 | 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 | } | |
8bc69b4b SG |
748 | } |
749 | ||
750 | return 0; | |
751 | } | |
752 | ||
699b0acb SG |
753 | int 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 | } | |
c4fea34f SG |
786 | 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 | } | |
699b0acb SG |
796 | } |
797 | } | |
798 | ||
799 | return 0; | |
800 | } | |
2e593897 SG |
801 | |
802 | int 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: | |
c4fea34f | 815 | case SCENEOBJT_TEXTLINE: |
2e593897 SG |
816 | 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 | } | |
756c9559 SG |
830 | |
831 | void scene_set_highlight_id(struct scene *scn, uint id) | |
832 | { | |
833 | scn->highlight_id = id; | |
834 | } | |
835 | ||
836 | void scene_highlight_first(struct scene *scn) | |
837 | { | |
838 | struct scene_obj *obj; | |
839 | ||
840 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
d88edd2b | 841 | if (scene_obj_can_highlight(obj)) { |
756c9559 SG |
842 | scene_set_highlight_id(scn, obj->id); |
843 | return; | |
756c9559 SG |
844 | } |
845 | } | |
846 | } | |
847 | ||
93c901bc SG |
848 | static 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 | ||
756c9559 SG |
869 | int scene_set_open(struct scene *scn, uint id, bool open) |
870 | { | |
93c901bc | 871 | struct scene_obj *obj; |
756c9559 SG |
872 | int ret; |
873 | ||
93c901bc SG |
874 | 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 | ||
756c9559 SG |
884 | 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 | } | |
f2eb6ad5 SG |
891 | |
892 | int 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 | } | |
8bc69b4b SG |
907 | |
908 | int 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 | } |