]>
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 <[email protected]> | |
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 | ||
def898c4 | 73 | int scene_title_set(struct scene *scn, uint id) |
5e2607ae | 74 | { |
def898c4 | 75 | scn->title_id = id; |
5e2607ae SG |
76 | |
77 | return 0; | |
78 | } | |
79 | ||
80 | int scene_obj_count(struct scene *scn) | |
81 | { | |
be222ac0 | 82 | return list_count_nodes(&scn->obj_head); |
5e2607ae SG |
83 | } |
84 | ||
633b3dc7 | 85 | void *scene_obj_find(const struct scene *scn, uint id, enum scene_obj_t type) |
5e2607ae SG |
86 | { |
87 | struct scene_obj *obj; | |
88 | ||
89 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
90 | if (obj->id == id && | |
91 | (type == SCENEOBJT_NONE || obj->type == type)) | |
92 | return obj; | |
93 | } | |
94 | ||
95 | return NULL; | |
96 | } | |
97 | ||
a0874dc4 SG |
98 | void *scene_obj_find_by_name(struct scene *scn, const char *name) |
99 | { | |
100 | struct scene_obj *obj; | |
101 | ||
102 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
103 | if (!strcmp(name, obj->name)) | |
104 | return obj; | |
105 | } | |
106 | ||
107 | return NULL; | |
108 | } | |
109 | ||
5e2607ae SG |
110 | int scene_obj_add(struct scene *scn, const char *name, uint id, |
111 | enum scene_obj_t type, uint size, struct scene_obj **objp) | |
112 | { | |
113 | struct scene_obj *obj; | |
114 | ||
115 | obj = calloc(1, size); | |
116 | if (!obj) | |
117 | return log_msg_ret("obj", -ENOMEM); | |
118 | obj->name = strdup(name); | |
119 | if (!obj->name) { | |
120 | free(obj); | |
121 | return log_msg_ret("name", -ENOMEM); | |
122 | } | |
123 | ||
124 | obj->id = resolve_id(scn->expo, id); | |
125 | obj->scene = scn; | |
126 | obj->type = type; | |
127 | list_add_tail(&obj->sibling, &scn->obj_head); | |
128 | *objp = obj; | |
129 | ||
130 | return obj->id; | |
131 | } | |
132 | ||
133 | int scene_img(struct scene *scn, const char *name, uint id, char *data, | |
134 | struct scene_obj_img **imgp) | |
135 | { | |
136 | struct scene_obj_img *img; | |
137 | int ret; | |
138 | ||
139 | ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE, | |
140 | sizeof(struct scene_obj_img), | |
141 | (struct scene_obj **)&img); | |
142 | if (ret < 0) | |
9767de7b | 143 | return log_msg_ret("obj", ret); |
5e2607ae SG |
144 | |
145 | img->data = data; | |
146 | ||
147 | if (imgp) | |
148 | *imgp = img; | |
149 | ||
150 | return img->obj.id; | |
151 | } | |
152 | ||
153 | int scene_txt(struct scene *scn, const char *name, uint id, uint str_id, | |
154 | struct scene_obj_txt **txtp) | |
155 | { | |
156 | struct scene_obj_txt *txt; | |
157 | int ret; | |
158 | ||
159 | ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT, | |
160 | sizeof(struct scene_obj_txt), | |
161 | (struct scene_obj **)&txt); | |
162 | if (ret < 0) | |
9767de7b | 163 | return log_msg_ret("obj", ret); |
5e2607ae SG |
164 | |
165 | txt->str_id = str_id; | |
166 | ||
167 | if (txtp) | |
168 | *txtp = txt; | |
169 | ||
170 | return txt->obj.id; | |
171 | } | |
172 | ||
173 | int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id, | |
174 | const char *str, struct scene_obj_txt **txtp) | |
175 | { | |
176 | struct scene_obj_txt *txt; | |
177 | int ret; | |
178 | ||
179 | ret = expo_str(scn->expo, name, str_id, str); | |
180 | if (ret < 0) | |
181 | return log_msg_ret("str", ret); | |
408011c2 | 182 | if (str_id && ret != str_id) |
5e2607ae | 183 | return log_msg_ret("id", -EEXIST); |
408011c2 | 184 | str_id = ret; |
5e2607ae SG |
185 | |
186 | ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT, | |
187 | sizeof(struct scene_obj_txt), | |
188 | (struct scene_obj **)&txt); | |
189 | if (ret < 0) | |
9767de7b | 190 | return log_msg_ret("obj", ret); |
5e2607ae SG |
191 | |
192 | txt->str_id = str_id; | |
193 | ||
194 | if (txtp) | |
195 | *txtp = txt; | |
196 | ||
197 | return txt->obj.id; | |
198 | } | |
199 | ||
200 | int scene_txt_set_font(struct scene *scn, uint id, const char *font_name, | |
201 | uint font_size) | |
202 | { | |
203 | struct scene_obj_txt *txt; | |
204 | ||
205 | txt = scene_obj_find(scn, id, SCENEOBJT_TEXT); | |
206 | if (!txt) | |
207 | return log_msg_ret("find", -ENOENT); | |
208 | txt->font_name = font_name; | |
209 | txt->font_size = font_size; | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
214 | int scene_obj_set_pos(struct scene *scn, uint id, int x, int y) | |
215 | { | |
216 | struct scene_obj *obj; | |
217 | ||
218 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); | |
219 | if (!obj) | |
220 | return log_msg_ret("find", -ENOENT); | |
ae45d6cf SG |
221 | obj->dim.x = x; |
222 | obj->dim.y = y; | |
5e2607ae SG |
223 | |
224 | return 0; | |
225 | } | |
226 | ||
699b0acb SG |
227 | int scene_obj_set_size(struct scene *scn, uint id, int w, int h) |
228 | { | |
229 | struct scene_obj *obj; | |
230 | ||
231 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); | |
232 | if (!obj) | |
233 | return log_msg_ret("find", -ENOENT); | |
234 | obj->dim.w = w; | |
235 | obj->dim.h = h; | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
5e2607ae | 240 | int scene_obj_set_hide(struct scene *scn, uint id, bool hide) |
ce72c9ec SG |
241 | { |
242 | int ret; | |
243 | ||
244 | ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE, | |
245 | hide ? SCENEOF_HIDE : 0); | |
246 | if (ret) | |
247 | return log_msg_ret("flg", ret); | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set) | |
5e2607ae SG |
253 | { |
254 | struct scene_obj *obj; | |
255 | ||
256 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); | |
257 | if (!obj) | |
258 | return log_msg_ret("find", -ENOENT); | |
ce72c9ec SG |
259 | obj->flags &= ~clr; |
260 | obj->flags |= set; | |
5e2607ae SG |
261 | |
262 | return 0; | |
263 | } | |
264 | ||
265 | int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) | |
266 | { | |
267 | struct scene_obj *obj; | |
268 | ||
269 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); | |
270 | if (!obj) | |
271 | return log_msg_ret("find", -ENOENT); | |
272 | ||
273 | switch (obj->type) { | |
274 | case SCENEOBJT_NONE: | |
275 | case SCENEOBJT_MENU: | |
c4fea34f | 276 | case SCENEOBJT_TEXTLINE: |
5e2607ae SG |
277 | break; |
278 | case SCENEOBJT_IMAGE: { | |
279 | struct scene_obj_img *img = (struct scene_obj_img *)obj; | |
280 | ulong width, height; | |
281 | uint bpix; | |
282 | ||
283 | video_bmp_get_info(img->data, &width, &height, &bpix); | |
284 | if (widthp) | |
285 | *widthp = width; | |
286 | return height; | |
287 | } | |
288 | case SCENEOBJT_TEXT: { | |
289 | struct scene_obj_txt *txt = (struct scene_obj_txt *)obj; | |
290 | struct expo *exp = scn->expo; | |
50f02037 SG |
291 | struct vidconsole_bbox bbox; |
292 | const char *str; | |
293 | int len, ret; | |
294 | ||
295 | str = expo_get_str(exp, txt->str_id); | |
296 | if (!str) | |
297 | return log_msg_ret("str", -ENOENT); | |
298 | len = strlen(str); | |
299 | ||
300 | /* if there is no console, make it up */ | |
301 | if (!exp->cons) { | |
302 | if (widthp) | |
303 | *widthp = 8 * len; | |
304 | return 16; | |
305 | } | |
306 | ||
307 | ret = vidconsole_measure(scn->expo->cons, txt->font_name, | |
308 | txt->font_size, str, &bbox); | |
309 | if (ret) | |
310 | return log_msg_ret("mea", ret); | |
5e2607ae | 311 | if (widthp) |
50f02037 SG |
312 | *widthp = bbox.x1; |
313 | ||
314 | return bbox.y1; | |
5e2607ae SG |
315 | } |
316 | } | |
317 | ||
318 | return 0; | |
319 | } | |
320 | ||
94598d5b SG |
321 | /** |
322 | * scene_render_background() - Render the background for an object | |
323 | * | |
324 | * @obj: Object to render | |
c4fea34f SG |
325 | * @box_only: true to show a box around the object, but keep the normal |
326 | * background colour inside | |
94598d5b | 327 | */ |
c4fea34f | 328 | static void scene_render_background(struct scene_obj *obj, bool box_only) |
94598d5b SG |
329 | { |
330 | struct expo *exp = obj->scene->expo; | |
331 | const struct expo_theme *theme = &exp->theme; | |
332 | struct vidconsole_bbox bbox, label_bbox; | |
333 | struct udevice *dev = exp->display; | |
334 | struct video_priv *vid_priv; | |
335 | struct udevice *cons = exp->cons; | |
336 | struct vidconsole_colour old; | |
337 | enum colour_idx fore, back; | |
338 | uint inset = theme->menu_inset; | |
339 | ||
340 | /* draw a background for the object */ | |
341 | if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) { | |
52c19173 | 342 | fore = VID_DARK_GREY; |
94598d5b SG |
343 | back = VID_WHITE; |
344 | } else { | |
345 | fore = VID_LIGHT_GRAY; | |
346 | back = VID_BLACK; | |
347 | } | |
348 | ||
349 | /* see if this object wants to render a background */ | |
350 | if (scene_obj_calc_bbox(obj, &bbox, &label_bbox)) | |
351 | return; | |
352 | ||
353 | vidconsole_push_colour(cons, fore, back, &old); | |
354 | vid_priv = dev_get_uclass_priv(dev); | |
355 | video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset, | |
356 | label_bbox.x1 + inset, label_bbox.y1 + inset, | |
357 | vid_priv->colour_fg); | |
358 | vidconsole_pop_colour(cons, &old); | |
c4fea34f SG |
359 | if (box_only) { |
360 | video_fill_part(dev, label_bbox.x0, label_bbox.y0, | |
361 | label_bbox.x1, label_bbox.y1, | |
362 | vid_priv->colour_bg); | |
363 | } | |
94598d5b SG |
364 | } |
365 | ||
5e2607ae SG |
366 | /** |
367 | * scene_obj_render() - Render an object | |
368 | * | |
369 | */ | |
370 | static int scene_obj_render(struct scene_obj *obj, bool text_mode) | |
371 | { | |
372 | struct scene *scn = obj->scene; | |
373 | struct expo *exp = scn->expo; | |
7230fdb3 | 374 | const struct expo_theme *theme = &exp->theme; |
42b18494 SG |
375 | struct udevice *dev = exp->display; |
376 | struct udevice *cons = text_mode ? NULL : exp->cons; | |
5e2607ae SG |
377 | int x, y, ret; |
378 | ||
ae45d6cf SG |
379 | x = obj->dim.x; |
380 | y = obj->dim.y; | |
5e2607ae SG |
381 | |
382 | switch (obj->type) { | |
383 | case SCENEOBJT_NONE: | |
384 | break; | |
385 | case SCENEOBJT_IMAGE: { | |
386 | struct scene_obj_img *img = (struct scene_obj_img *)obj; | |
387 | ||
388 | if (!cons) | |
389 | return -ENOTSUPP; | |
390 | ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y, | |
391 | true); | |
392 | if (ret < 0) | |
393 | return log_msg_ret("img", ret); | |
394 | break; | |
395 | } | |
396 | case SCENEOBJT_TEXT: { | |
397 | struct scene_obj_txt *txt = (struct scene_obj_txt *)obj; | |
398 | const char *str; | |
399 | ||
400 | if (!cons) | |
401 | return -ENOTSUPP; | |
402 | ||
403 | if (txt->font_name || txt->font_size) { | |
404 | ret = vidconsole_select_font(cons, | |
405 | txt->font_name, | |
406 | txt->font_size); | |
407 | } else { | |
408 | ret = vidconsole_select_font(cons, NULL, 0); | |
409 | } | |
410 | if (ret && ret != -ENOSYS) | |
411 | return log_msg_ret("font", ret); | |
5e2607ae | 412 | str = expo_get_str(exp, txt->str_id); |
756c9559 SG |
413 | if (str) { |
414 | struct video_priv *vid_priv; | |
415 | struct vidconsole_colour old; | |
416 | enum colour_idx fore, back; | |
417 | ||
418 | if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) { | |
419 | fore = VID_BLACK; | |
420 | back = VID_WHITE; | |
421 | } else { | |
422 | fore = VID_LIGHT_GRAY; | |
423 | back = VID_BLACK; | |
424 | } | |
425 | ||
426 | vid_priv = dev_get_uclass_priv(dev); | |
427 | if (obj->flags & SCENEOF_POINT) { | |
428 | vidconsole_push_colour(cons, fore, back, &old); | |
7230fdb3 SG |
429 | video_fill_part(dev, x - theme->menu_inset, y, |
430 | x + obj->dim.w, | |
431 | y + obj->dim.h, | |
756c9559 SG |
432 | vid_priv->colour_bg); |
433 | } | |
434 | vidconsole_set_cursor_pos(cons, x, y); | |
5e2607ae | 435 | vidconsole_put_string(cons, str); |
756c9559 SG |
436 | if (obj->flags & SCENEOF_POINT) |
437 | vidconsole_pop_colour(cons, &old); | |
438 | } | |
5e2607ae SG |
439 | break; |
440 | } | |
441 | case SCENEOBJT_MENU: { | |
442 | struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; | |
756c9559 SG |
443 | |
444 | if (exp->popup && (obj->flags & SCENEOF_OPEN)) { | |
445 | if (!cons) | |
446 | return -ENOTSUPP; | |
447 | ||
448 | /* draw a background behind the menu items */ | |
c4fea34f | 449 | scene_render_background(obj, false); |
756c9559 | 450 | } |
5e2607ae SG |
451 | /* |
452 | * With a vidconsole, the text and item pointer are rendered as | |
453 | * normal objects so we don't need to do anything here. The menu | |
454 | * simply controls where they are positioned. | |
455 | */ | |
456 | if (cons) | |
457 | return -ENOTSUPP; | |
458 | ||
459 | ret = scene_menu_display(menu); | |
460 | if (ret < 0) | |
461 | return log_msg_ret("img", ret); | |
462 | ||
463 | break; | |
464 | } | |
c4fea34f SG |
465 | case SCENEOBJT_TEXTLINE: |
466 | if (obj->flags & SCENEOF_OPEN) | |
467 | scene_render_background(obj, true); | |
468 | break; | |
5e2607ae SG |
469 | } |
470 | ||
471 | return 0; | |
472 | } | |
473 | ||
474 | int scene_arrange(struct scene *scn) | |
475 | { | |
476 | struct scene_obj *obj; | |
477 | int ret; | |
478 | ||
479 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
fd6073ac SG |
480 | switch (obj->type) { |
481 | case SCENEOBJT_NONE: | |
482 | case SCENEOBJT_IMAGE: | |
483 | case SCENEOBJT_TEXT: | |
484 | break; | |
485 | case SCENEOBJT_MENU: { | |
5e2607ae SG |
486 | struct scene_obj_menu *menu; |
487 | ||
488 | menu = (struct scene_obj_menu *)obj, | |
489 | ret = scene_menu_arrange(scn, menu); | |
490 | if (ret) | |
491 | return log_msg_ret("arr", ret); | |
fd6073ac SG |
492 | break; |
493 | } | |
c4fea34f SG |
494 | case SCENEOBJT_TEXTLINE: { |
495 | struct scene_obj_textline *tline; | |
496 | ||
497 | tline = (struct scene_obj_textline *)obj, | |
498 | ret = scene_textline_arrange(scn, tline); | |
499 | if (ret) | |
500 | return log_msg_ret("arr", ret); | |
501 | break; | |
502 | } | |
5e2607ae SG |
503 | } |
504 | } | |
505 | ||
506 | return 0; | |
507 | } | |
508 | ||
4c87e073 SG |
509 | int scene_render_deps(struct scene *scn, uint id) |
510 | { | |
511 | struct scene_obj *obj; | |
512 | int ret; | |
513 | ||
514 | if (!id) | |
515 | return 0; | |
516 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); | |
517 | if (!obj) | |
518 | return log_msg_ret("obj", -ENOENT); | |
519 | ||
520 | if (!(obj->flags & SCENEOF_HIDE)) { | |
521 | ret = scene_obj_render(obj, false); | |
522 | if (ret && ret != -ENOTSUPP) | |
523 | return log_msg_ret("ren", ret); | |
524 | ||
fd6073ac SG |
525 | switch (obj->type) { |
526 | case SCENEOBJT_NONE: | |
527 | case SCENEOBJT_IMAGE: | |
528 | case SCENEOBJT_TEXT: | |
529 | break; | |
530 | case SCENEOBJT_MENU: | |
4c87e073 SG |
531 | scene_menu_render_deps(scn, |
532 | (struct scene_obj_menu *)obj); | |
fd6073ac | 533 | break; |
c4fea34f SG |
534 | case SCENEOBJT_TEXTLINE: |
535 | scene_textline_render_deps(scn, | |
536 | (struct scene_obj_textline *)obj); | |
537 | break; | |
fd6073ac | 538 | } |
4c87e073 SG |
539 | } |
540 | ||
541 | return 0; | |
542 | } | |
543 | ||
5e2607ae SG |
544 | int scene_render(struct scene *scn) |
545 | { | |
546 | struct expo *exp = scn->expo; | |
547 | struct scene_obj *obj; | |
548 | int ret; | |
549 | ||
550 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
ce72c9ec | 551 | if (!(obj->flags & SCENEOF_HIDE)) { |
5e2607ae SG |
552 | ret = scene_obj_render(obj, exp->text_mode); |
553 | if (ret && ret != -ENOTSUPP) | |
554 | return log_msg_ret("ren", ret); | |
555 | } | |
556 | } | |
557 | ||
4c87e073 SG |
558 | /* render any highlighted object on top of the others */ |
559 | if (scn->highlight_id && !exp->text_mode) { | |
560 | ret = scene_render_deps(scn, scn->highlight_id); | |
561 | if (ret && ret != -ENOTSUPP) | |
562 | return log_msg_ret("dep", ret); | |
563 | } | |
564 | ||
5e2607ae SG |
565 | return 0; |
566 | } | |
567 | ||
4e64beeb SG |
568 | /** |
569 | * send_key_obj() - Handle a keypress for moving between objects | |
570 | * | |
571 | * @scn: Scene to receive the key | |
572 | * @key: Key to send (KEYCODE_UP) | |
573 | * @event: Returns resulting event from this keypress | |
574 | * Returns: 0 if OK, -ve on error | |
575 | */ | |
576 | static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key, | |
577 | struct expo_action *event) | |
578 | { | |
579 | switch (key) { | |
580 | case BKEY_UP: | |
581 | while (obj != list_first_entry(&scn->obj_head, struct scene_obj, | |
582 | sibling)) { | |
583 | obj = list_entry(obj->sibling.prev, | |
584 | struct scene_obj, sibling); | |
d88edd2b | 585 | if (scene_obj_can_highlight(obj)) { |
4e64beeb SG |
586 | event->type = EXPOACT_POINT_OBJ; |
587 | event->select.id = obj->id; | |
588 | log_debug("up to obj %d\n", event->select.id); | |
589 | break; | |
590 | } | |
591 | } | |
592 | break; | |
593 | case BKEY_DOWN: | |
594 | while (!list_is_last(&obj->sibling, &scn->obj_head)) { | |
595 | obj = list_entry(obj->sibling.next, struct scene_obj, | |
596 | sibling); | |
d88edd2b | 597 | if (scene_obj_can_highlight(obj)) { |
4e64beeb SG |
598 | event->type = EXPOACT_POINT_OBJ; |
599 | event->select.id = obj->id; | |
600 | log_debug("down to obj %d\n", event->select.id); | |
601 | break; | |
602 | } | |
603 | } | |
604 | break; | |
605 | case BKEY_SELECT: | |
d88edd2b | 606 | if (scene_obj_can_highlight(obj)) { |
4e64beeb SG |
607 | event->type = EXPOACT_OPEN; |
608 | event->select.id = obj->id; | |
609 | log_debug("open obj %d\n", event->select.id); | |
610 | } | |
611 | break; | |
612 | case BKEY_QUIT: | |
613 | event->type = EXPOACT_QUIT; | |
614 | log_debug("obj quit\n"); | |
615 | break; | |
616 | } | |
617 | } | |
618 | ||
5e2607ae SG |
619 | int scene_send_key(struct scene *scn, int key, struct expo_action *event) |
620 | { | |
621 | struct scene_obj *obj; | |
622 | int ret; | |
623 | ||
4e64beeb SG |
624 | event->type = EXPOACT_NONE; |
625 | ||
626 | /* | |
627 | * In 'popup' mode, arrow keys move betwen objects, unless a menu is | |
628 | * opened | |
629 | */ | |
630 | if (scn->expo->popup) { | |
631 | obj = NULL; | |
632 | if (scn->highlight_id) { | |
633 | obj = scene_obj_find(scn, scn->highlight_id, | |
634 | SCENEOBJT_NONE); | |
635 | } | |
636 | if (!obj) | |
637 | return 0; | |
638 | ||
639 | if (!(obj->flags & SCENEOF_OPEN)) { | |
640 | send_key_obj(scn, obj, key, event); | |
641 | return 0; | |
642 | } | |
643 | ||
fd6073ac SG |
644 | switch (obj->type) { |
645 | case SCENEOBJT_NONE: | |
646 | case SCENEOBJT_IMAGE: | |
647 | case SCENEOBJT_TEXT: | |
648 | break; | |
649 | case SCENEOBJT_MENU: { | |
650 | struct scene_obj_menu *menu; | |
651 | ||
652 | menu = (struct scene_obj_menu *)obj, | |
653 | ret = scene_menu_send_key(scn, menu, key, event); | |
654 | if (ret) | |
655 | return log_msg_ret("key", ret); | |
656 | break; | |
657 | } | |
c4fea34f SG |
658 | case SCENEOBJT_TEXTLINE: { |
659 | struct scene_obj_textline *tline; | |
660 | ||
661 | tline = (struct scene_obj_textline *)obj, | |
662 | ret = scene_textline_send_key(scn, tline, key, event); | |
663 | if (ret) | |
664 | return log_msg_ret("key", ret); | |
665 | break; | |
666 | } | |
fd6073ac | 667 | } |
4e64beeb SG |
668 | return 0; |
669 | } | |
670 | ||
5e2607ae SG |
671 | list_for_each_entry(obj, &scn->obj_head, sibling) { |
672 | if (obj->type == SCENEOBJT_MENU) { | |
673 | struct scene_obj_menu *menu; | |
674 | ||
675 | menu = (struct scene_obj_menu *)obj, | |
676 | ret = scene_menu_send_key(scn, menu, key, event); | |
677 | if (ret) | |
678 | return log_msg_ret("key", ret); | |
5e2607ae SG |
679 | break; |
680 | } | |
681 | } | |
682 | ||
683 | return 0; | |
684 | } | |
699b0acb | 685 | |
8bc69b4b SG |
686 | int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox, |
687 | struct vidconsole_bbox *label_bbox) | |
688 | { | |
689 | switch (obj->type) { | |
690 | case SCENEOBJT_NONE: | |
691 | case SCENEOBJT_IMAGE: | |
692 | case SCENEOBJT_TEXT: | |
693 | return -ENOSYS; | |
694 | case SCENEOBJT_MENU: { | |
695 | struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; | |
696 | ||
697 | scene_menu_calc_bbox(menu, bbox, label_bbox); | |
698 | break; | |
699 | } | |
c4fea34f SG |
700 | case SCENEOBJT_TEXTLINE: { |
701 | struct scene_obj_textline *tline; | |
702 | ||
703 | tline = (struct scene_obj_textline *)obj; | |
704 | scene_textline_calc_bbox(tline, bbox, label_bbox); | |
705 | break; | |
706 | } | |
8bc69b4b SG |
707 | } |
708 | ||
709 | return 0; | |
710 | } | |
711 | ||
699b0acb SG |
712 | int scene_calc_dims(struct scene *scn, bool do_menus) |
713 | { | |
714 | struct scene_obj *obj; | |
715 | int ret; | |
716 | ||
717 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
718 | switch (obj->type) { | |
719 | case SCENEOBJT_NONE: | |
720 | case SCENEOBJT_TEXT: | |
721 | case SCENEOBJT_IMAGE: { | |
722 | int width; | |
723 | ||
724 | if (!do_menus) { | |
725 | ret = scene_obj_get_hw(scn, obj->id, &width); | |
726 | if (ret < 0) | |
727 | return log_msg_ret("get", ret); | |
728 | obj->dim.w = width; | |
729 | obj->dim.h = ret; | |
730 | } | |
731 | break; | |
732 | } | |
733 | case SCENEOBJT_MENU: { | |
734 | struct scene_obj_menu *menu; | |
735 | ||
736 | if (do_menus) { | |
737 | menu = (struct scene_obj_menu *)obj; | |
738 | ||
739 | ret = scene_menu_calc_dims(menu); | |
740 | if (ret) | |
741 | return log_msg_ret("men", ret); | |
742 | } | |
743 | break; | |
744 | } | |
c4fea34f SG |
745 | case SCENEOBJT_TEXTLINE: { |
746 | struct scene_obj_textline *tline; | |
747 | ||
748 | tline = (struct scene_obj_textline *)obj; | |
749 | ret = scene_textline_calc_dims(tline); | |
750 | if (ret) | |
751 | return log_msg_ret("men", ret); | |
752 | ||
753 | break; | |
754 | } | |
699b0acb SG |
755 | } |
756 | } | |
757 | ||
758 | return 0; | |
759 | } | |
2e593897 SG |
760 | |
761 | int scene_apply_theme(struct scene *scn, struct expo_theme *theme) | |
762 | { | |
763 | struct scene_obj *obj; | |
764 | int ret; | |
765 | ||
766 | /* Avoid error-checking optional items */ | |
767 | scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size); | |
768 | ||
769 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
770 | switch (obj->type) { | |
771 | case SCENEOBJT_NONE: | |
772 | case SCENEOBJT_IMAGE: | |
773 | case SCENEOBJT_MENU: | |
c4fea34f | 774 | case SCENEOBJT_TEXTLINE: |
2e593897 SG |
775 | break; |
776 | case SCENEOBJT_TEXT: | |
777 | scene_txt_set_font(scn, obj->id, NULL, | |
778 | theme->font_size); | |
779 | break; | |
780 | } | |
781 | } | |
782 | ||
783 | ret = scene_arrange(scn); | |
784 | if (ret) | |
785 | return log_msg_ret("arr", ret); | |
786 | ||
787 | return 0; | |
788 | } | |
756c9559 SG |
789 | |
790 | void scene_set_highlight_id(struct scene *scn, uint id) | |
791 | { | |
792 | scn->highlight_id = id; | |
793 | } | |
794 | ||
795 | void scene_highlight_first(struct scene *scn) | |
796 | { | |
797 | struct scene_obj *obj; | |
798 | ||
799 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
d88edd2b | 800 | if (scene_obj_can_highlight(obj)) { |
756c9559 SG |
801 | scene_set_highlight_id(scn, obj->id); |
802 | return; | |
756c9559 SG |
803 | } |
804 | } | |
805 | } | |
806 | ||
93c901bc SG |
807 | static int scene_obj_open(struct scene *scn, struct scene_obj *obj) |
808 | { | |
809 | int ret; | |
810 | ||
811 | switch (obj->type) { | |
812 | case SCENEOBJT_NONE: | |
813 | case SCENEOBJT_IMAGE: | |
814 | case SCENEOBJT_MENU: | |
815 | case SCENEOBJT_TEXT: | |
816 | break; | |
817 | case SCENEOBJT_TEXTLINE: | |
818 | ret = scene_textline_open(scn, | |
819 | (struct scene_obj_textline *)obj); | |
820 | if (ret) | |
821 | return log_msg_ret("op", ret); | |
822 | break; | |
823 | } | |
824 | ||
825 | return 0; | |
826 | } | |
827 | ||
756c9559 SG |
828 | int scene_set_open(struct scene *scn, uint id, bool open) |
829 | { | |
93c901bc | 830 | struct scene_obj *obj; |
756c9559 SG |
831 | int ret; |
832 | ||
93c901bc SG |
833 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); |
834 | if (!obj) | |
835 | return log_msg_ret("find", -ENOENT); | |
836 | ||
837 | if (open) { | |
838 | ret = scene_obj_open(scn, obj); | |
839 | if (ret) | |
840 | return log_msg_ret("op", ret); | |
841 | } | |
842 | ||
756c9559 SG |
843 | ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN, |
844 | open ? SCENEOF_OPEN : 0); | |
845 | if (ret) | |
846 | return log_msg_ret("flg", ret); | |
847 | ||
848 | return 0; | |
849 | } | |
f2eb6ad5 SG |
850 | |
851 | int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter, | |
852 | void *priv) | |
853 | { | |
854 | struct scene_obj *obj; | |
855 | ||
856 | list_for_each_entry(obj, &scn->obj_head, sibling) { | |
857 | int ret; | |
858 | ||
859 | ret = iter(obj, priv); | |
860 | if (ret) | |
861 | return log_msg_ret("itr", ret); | |
862 | } | |
863 | ||
864 | return 0; | |
865 | } | |
8bc69b4b SG |
866 | |
867 | int scene_bbox_union(struct scene *scn, uint id, int inset, | |
868 | struct vidconsole_bbox *bbox) | |
869 | { | |
870 | struct scene_obj *obj; | |
871 | ||
872 | if (!id) | |
873 | return 0; | |
874 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); | |
875 | if (!obj) | |
876 | return log_msg_ret("obj", -ENOENT); | |
877 | if (bbox->valid) { | |
878 | bbox->x0 = min(bbox->x0, obj->dim.x - inset); | |
879 | bbox->y0 = min(bbox->y0, obj->dim.y); | |
880 | bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset); | |
881 | bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h); | |
882 | } else { | |
883 | bbox->x0 = obj->dim.x - inset; | |
884 | bbox->y0 = obj->dim.y; | |
885 | bbox->x1 = obj->dim.x + obj->dim.w + inset; | |
886 | bbox->y1 = obj->dim.y + obj->dim.h; | |
887 | bbox->valid = true; | |
888 | } | |
889 | ||
890 | return 0; | |
891 | } |