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