blob: e0dcd0a4e041c0f8a8160b2f75db6eb22445dde9 [file] [log] [blame]
Simon Glass9f513932023-01-06 08:52:38 -06001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Implementation of a menu in a scene
4 *
5 * Copyright 2022 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
Simon Glassfe4c1e22023-06-01 10:22:43 -06009#define LOG_CATEGORY LOGC_EXPO
Simon Glass9f513932023-01-06 08:52:38 -060010
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
22static void scene_menuitem_destroy(struct scene_menitem *item)
23{
24 free(item->name);
25 free(item);
26}
27
28void scene_menu_destroy(struct scene_obj_menu *menu)
29{
30 struct scene_menitem *item, *next;
31
32 list_for_each_entry_safe(item, next, &menu->item_head, sibling)
33 scene_menuitem_destroy(item);
34}
35
Simon Glass5fd4f782023-08-14 16:40:32 -060036struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu,
37 int id)
Simon Glassc55eeba2023-06-01 10:22:54 -060038{
39 struct scene_menitem *item;
40
41 list_for_each_entry(item, &menu->item_head, sibling) {
42 if (item->id == id)
43 return item;
44 }
45
46 return NULL;
47}
48
Simon Glass4462fa32023-08-14 16:40:38 -060049struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu,
50 uint seq)
51{
52 struct scene_menitem *item;
53 uint i;
54
55 i = 0;
56 list_for_each_entry(item, &menu->item_head, sibling) {
57 if (i == seq)
58 return item;
59 i++;
60 }
61
62 return NULL;
63}
64
Simon Glassc55eeba2023-06-01 10:22:54 -060065/**
66 * update_pointers() - Update the pointer object and handle highlights
67 *
68 * @menu: Menu to update
69 * @id: ID of menu item to select/deselect
70 * @point: true if @id is being selected, false if it is being deselected
71 */
72static int update_pointers(struct scene_obj_menu *menu, uint id, bool point)
73{
74 struct scene *scn = menu->obj.scene;
Simon Glassd353b752023-06-01 10:22:55 -060075 const bool stack = scn->expo->popup;
Simon Glassc55eeba2023-06-01 10:22:54 -060076 const struct scene_menitem *item;
77 int ret;
78
79 item = scene_menuitem_find(menu, id);
80 if (!item)
81 return log_msg_ret("itm", -ENOENT);
82
83 /* adjust the pointer object to point to the selected item */
84 if (menu->pointer_id && item && point) {
85 struct scene_obj *label;
86
87 label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
88
89 ret = scene_obj_set_pos(scn, menu->pointer_id,
90 menu->obj.dim.x + 200, label->dim.y);
91 if (ret < 0)
92 return log_msg_ret("ptr", ret);
93 }
94
Simon Glassd353b752023-06-01 10:22:55 -060095 if (stack) {
96 point &= scn->highlight_id == menu->obj.id;
97 scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT,
98 point ? SCENEOF_POINT : 0);
99 }
100
Simon Glassc55eeba2023-06-01 10:22:54 -0600101 return 0;
102}
103
Simon Glass9f513932023-01-06 08:52:38 -0600104/**
105 * menu_point_to_item() - Point to a particular menu item
106 *
107 * Sets the currently pointed-to / highlighted menu item
108 */
109static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id)
110{
Simon Glassc55eeba2023-06-01 10:22:54 -0600111 if (menu->cur_item_id)
112 update_pointers(menu, menu->cur_item_id, false);
Simon Glass9f513932023-01-06 08:52:38 -0600113 menu->cur_item_id = item_id;
Simon Glassc55eeba2023-06-01 10:22:54 -0600114 update_pointers(menu, item_id, true);
Simon Glass9f513932023-01-06 08:52:38 -0600115}
116
Simon Glass86f1ac52023-06-01 10:23:00 -0600117static int scene_bbox_union(struct scene *scn, uint id, int inset,
Simon Glass7a960052023-06-01 10:22:52 -0600118 struct vidconsole_bbox *bbox)
119{
120 struct scene_obj *obj;
121
122 if (!id)
123 return 0;
124 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
125 if (!obj)
126 return log_msg_ret("obj", -ENOENT);
127 if (bbox->valid) {
Simon Glass86f1ac52023-06-01 10:23:00 -0600128 bbox->x0 = min(bbox->x0, obj->dim.x - inset);
Simon Glass7a960052023-06-01 10:22:52 -0600129 bbox->y0 = min(bbox->y0, obj->dim.y);
Simon Glass86f1ac52023-06-01 10:23:00 -0600130 bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
Simon Glass7a960052023-06-01 10:22:52 -0600131 bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
132 } else {
Simon Glass86f1ac52023-06-01 10:23:00 -0600133 bbox->x0 = obj->dim.x - inset;
Simon Glass7a960052023-06-01 10:22:52 -0600134 bbox->y0 = obj->dim.y;
Simon Glass86f1ac52023-06-01 10:23:00 -0600135 bbox->x1 = obj->dim.x + obj->dim.w + inset;
Simon Glass7a960052023-06-01 10:22:52 -0600136 bbox->y1 = obj->dim.y + obj->dim.h;
137 bbox->valid = true;
138 }
139
140 return 0;
141}
142
143/**
144 * scene_menu_calc_bbox() - Calculate bounding boxes for the menu
145 *
146 * @menu: Menu to process
147 * @bbox: Returns bounding box of menu including prompts
148 * @label_bbox: Returns bounding box of labels
149 */
150static void scene_menu_calc_bbox(struct scene_obj_menu *menu,
151 struct vidconsole_bbox *bbox,
152 struct vidconsole_bbox *label_bbox)
153{
Simon Glass86f1ac52023-06-01 10:23:00 -0600154 const struct expo_theme *theme = &menu->obj.scene->expo->theme;
Simon Glass7a960052023-06-01 10:22:52 -0600155 const struct scene_menitem *item;
156
157 bbox->valid = false;
Simon Glass86f1ac52023-06-01 10:23:00 -0600158 scene_bbox_union(menu->obj.scene, menu->title_id, 0, bbox);
Simon Glass7a960052023-06-01 10:22:52 -0600159
160 label_bbox->valid = false;
161
162 list_for_each_entry(item, &menu->item_head, sibling) {
Simon Glass86f1ac52023-06-01 10:23:00 -0600163 scene_bbox_union(menu->obj.scene, item->label_id,
164 theme->menu_inset, bbox);
165 scene_bbox_union(menu->obj.scene, item->key_id, 0, bbox);
166 scene_bbox_union(menu->obj.scene, item->desc_id, 0, bbox);
167 scene_bbox_union(menu->obj.scene, item->preview_id, 0, bbox);
Simon Glass7a960052023-06-01 10:22:52 -0600168
169 /* Get the bounding box of all labels */
Simon Glass86f1ac52023-06-01 10:23:00 -0600170 scene_bbox_union(menu->obj.scene, item->label_id,
171 theme->menu_inset, label_bbox);
Simon Glass7a960052023-06-01 10:22:52 -0600172 }
Simon Glass86f1ac52023-06-01 10:23:00 -0600173
174 /*
175 * subtract the final menuitem's gap to keep the insert the same top
176 * and bottom
177 */
178 label_bbox->y1 -= theme->menuitem_gap_y;
Simon Glass7a960052023-06-01 10:22:52 -0600179}
180
181int scene_menu_calc_dims(struct scene_obj_menu *menu)
182{
183 struct vidconsole_bbox bbox, label_bbox;
184 const struct scene_menitem *item;
185
186 scene_menu_calc_bbox(menu, &bbox, &label_bbox);
187
188 /* Make all labels the same size */
189 if (label_bbox.valid) {
190 list_for_each_entry(item, &menu->item_head, sibling) {
191 scene_obj_set_size(menu->obj.scene, item->label_id,
192 label_bbox.x1 - label_bbox.x0,
193 label_bbox.y1 - label_bbox.y0);
194 }
195 }
196
197 if (bbox.valid) {
198 menu->obj.dim.w = bbox.x1 - bbox.x0;
199 menu->obj.dim.h = bbox.y1 - bbox.y0;
200 }
201
202 return 0;
203}
204
Simon Glass9f513932023-01-06 08:52:38 -0600205int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
206{
Simon Glassd353b752023-06-01 10:22:55 -0600207 const bool open = menu->obj.flags & SCENEOF_OPEN;
208 struct expo *exp = scn->expo;
209 const bool stack = exp->popup;
Simon Glass86f1ac52023-06-01 10:23:00 -0600210 const struct expo_theme *theme = &exp->theme;
Simon Glass9f513932023-01-06 08:52:38 -0600211 struct scene_menitem *item;
Simon Glassc55eeba2023-06-01 10:22:54 -0600212 uint sel_id;
Simon Glassd353b752023-06-01 10:22:55 -0600213 int x, y;
Simon Glass9f513932023-01-06 08:52:38 -0600214 int ret;
215
Simon Glassd353b752023-06-01 10:22:55 -0600216 x = menu->obj.dim.x;
Simon Glass7b043952023-06-01 10:22:49 -0600217 y = menu->obj.dim.y;
Simon Glass9f513932023-01-06 08:52:38 -0600218 if (menu->title_id) {
Simon Glass7b043952023-06-01 10:22:49 -0600219 ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y);
Simon Glass9f513932023-01-06 08:52:38 -0600220 if (ret < 0)
221 return log_msg_ret("tit", ret);
222
223 ret = scene_obj_get_hw(scn, menu->title_id, NULL);
224 if (ret < 0)
225 return log_msg_ret("hei", ret);
226
Simon Glassd353b752023-06-01 10:22:55 -0600227 if (stack)
228 x += 200;
229 else
230 y += ret * 2;
Simon Glass9f513932023-01-06 08:52:38 -0600231 }
232
233 /*
234 * Currently everything is hard-coded to particular columns so this
235 * won't work on small displays and looks strange if the font size is
236 * small. This can be updated once text measuring is supported in
237 * vidconsole
238 */
Simon Glassc55eeba2023-06-01 10:22:54 -0600239 sel_id = menu->cur_item_id;
Simon Glass9f513932023-01-06 08:52:38 -0600240 list_for_each_entry(item, &menu->item_head, sibling) {
Simon Glassd353b752023-06-01 10:22:55 -0600241 bool selected;
Simon Glass9f513932023-01-06 08:52:38 -0600242 int height;
243
Simon Glassd353b752023-06-01 10:22:55 -0600244 ret = scene_obj_get_hw(scn, item->label_id, NULL);
Simon Glass9f513932023-01-06 08:52:38 -0600245 if (ret < 0)
246 return log_msg_ret("get", ret);
247 height = ret;
248
249 if (item->flags & SCENEMIF_GAP_BEFORE)
250 y += height;
251
252 /* select an item if not done already */
Simon Glassc55eeba2023-06-01 10:22:54 -0600253 if (!sel_id)
254 sel_id = item->id;
Simon Glass9f513932023-01-06 08:52:38 -0600255
Simon Glassd353b752023-06-01 10:22:55 -0600256 selected = sel_id == item->id;
257
Simon Glass9f513932023-01-06 08:52:38 -0600258 /*
259 * Put the label on the left, then leave a space for the
260 * pointer, then the key and the description
261 */
Simon Glass86f1ac52023-06-01 10:23:00 -0600262 ret = scene_obj_set_pos(scn, item->label_id,
263 x + theme->menu_inset, y);
Simon Glass9f513932023-01-06 08:52:38 -0600264 if (ret < 0)
Simon Glassd353b752023-06-01 10:22:55 -0600265 return log_msg_ret("nam", ret);
266 scene_obj_set_hide(scn, item->label_id,
267 stack && !open && !selected);
Simon Glass9f513932023-01-06 08:52:38 -0600268
Simon Glassd353b752023-06-01 10:22:55 -0600269 if (item->key_id) {
270 ret = scene_obj_set_pos(scn, item->key_id, x + 230, y);
271 if (ret < 0)
272 return log_msg_ret("key", ret);
273 }
Simon Glass9f513932023-01-06 08:52:38 -0600274
Simon Glassd353b752023-06-01 10:22:55 -0600275 if (item->desc_id) {
276 ret = scene_obj_set_pos(scn, item->desc_id, x + 280, y);
277 if (ret < 0)
278 return log_msg_ret("des", ret);
279 }
Simon Glass9f513932023-01-06 08:52:38 -0600280
281 if (item->preview_id) {
282 bool hide;
283
284 /*
285 * put all previews on top of each other, on the right
286 * size of the display
287 */
288 ret = scene_obj_set_pos(scn, item->preview_id, -4, y);
289 if (ret < 0)
290 return log_msg_ret("prev", ret);
291
292 hide = menu->cur_item_id != item->id;
293 ret = scene_obj_set_hide(scn, item->preview_id, hide);
294 if (ret < 0)
295 return log_msg_ret("hid", ret);
296 }
297
Simon Glassd353b752023-06-01 10:22:55 -0600298 if (!stack || open)
Simon Glass86f1ac52023-06-01 10:23:00 -0600299 y += height + theme->menuitem_gap_y;
Simon Glass9f513932023-01-06 08:52:38 -0600300 }
301
Simon Glassc55eeba2023-06-01 10:22:54 -0600302 if (sel_id)
303 menu_point_to_item(menu, sel_id);
Simon Glass9f513932023-01-06 08:52:38 -0600304
305 return 0;
306}
307
308int scene_menu(struct scene *scn, const char *name, uint id,
309 struct scene_obj_menu **menup)
310{
311 struct scene_obj_menu *menu;
312 int ret;
313
314 ret = scene_obj_add(scn, name, id, SCENEOBJT_MENU,
315 sizeof(struct scene_obj_menu),
316 (struct scene_obj **)&menu);
317 if (ret < 0)
318 return log_msg_ret("obj", -ENOMEM);
319
320 if (menup)
321 *menup = menu;
322 INIT_LIST_HEAD(&menu->item_head);
323
Simon Glass9f513932023-01-06 08:52:38 -0600324 return menu->obj.id;
325}
326
327static struct scene_menitem *scene_menu_find_key(struct scene *scn,
328 struct scene_obj_menu *menu,
329 int key)
330{
331 struct scene_menitem *item;
332
333 list_for_each_entry(item, &menu->item_head, sibling) {
334 if (item->key_id) {
335 struct scene_obj_txt *txt;
336 const char *str;
337
338 txt = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
339 if (txt) {
340 str = expo_get_str(scn->expo, txt->str_id);
341 if (str && *str == key)
342 return item;
343 }
344 }
345 }
346
347 return NULL;
348}
349
350int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
351 struct expo_action *event)
352{
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600353 const bool open = menu->obj.flags & SCENEOF_OPEN;
Simon Glass9f513932023-01-06 08:52:38 -0600354 struct scene_menitem *item, *cur, *key_item;
355
356 cur = NULL;
357 key_item = NULL;
358
359 if (!list_empty(&menu->item_head)) {
360 list_for_each_entry(item, &menu->item_head, sibling) {
361 /* select an item if not done already */
362 if (menu->cur_item_id == item->id) {
363 cur = item;
364 break;
365 }
366 }
367 }
368
369 if (!cur)
370 return -ENOTTY;
371
372 switch (key) {
373 case BKEY_UP:
374 if (item != list_first_entry(&menu->item_head,
375 struct scene_menitem, sibling)) {
376 item = list_entry(item->sibling.prev,
377 struct scene_menitem, sibling);
Simon Glass719a3c62023-06-01 10:22:56 -0600378 event->type = EXPOACT_POINT_ITEM;
Simon Glass9f513932023-01-06 08:52:38 -0600379 event->select.id = item->id;
380 log_debug("up to item %d\n", event->select.id);
381 }
382 break;
383 case BKEY_DOWN:
384 if (!list_is_last(&item->sibling, &menu->item_head)) {
385 item = list_entry(item->sibling.next,
386 struct scene_menitem, sibling);
Simon Glass719a3c62023-06-01 10:22:56 -0600387 event->type = EXPOACT_POINT_ITEM;
Simon Glass9f513932023-01-06 08:52:38 -0600388 event->select.id = item->id;
389 log_debug("down to item %d\n", event->select.id);
390 }
391 break;
392 case BKEY_SELECT:
393 event->type = EXPOACT_SELECT;
394 event->select.id = item->id;
395 log_debug("select item %d\n", event->select.id);
396 break;
397 case BKEY_QUIT:
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600398 if (scn->expo->popup && open) {
399 event->type = EXPOACT_CLOSE;
400 event->select.id = menu->obj.id;
401 } else {
402 event->type = EXPOACT_QUIT;
403 log_debug("menu quit\n");
404 }
Simon Glass9f513932023-01-06 08:52:38 -0600405 break;
406 case '0'...'9':
407 key_item = scene_menu_find_key(scn, menu, key);
408 if (key_item) {
409 event->type = EXPOACT_SELECT;
410 event->select.id = key_item->id;
411 }
412 break;
413 }
414
415 menu_point_to_item(menu, item->id);
416
417 return 0;
418}
419
420int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
421 uint key_id, uint label_id, uint desc_id, uint preview_id,
422 uint flags, struct scene_menitem **itemp)
423{
424 struct scene_obj_menu *menu;
425 struct scene_menitem *item;
Simon Glass9f513932023-01-06 08:52:38 -0600426
427 menu = scene_obj_find(scn, menu_id, SCENEOBJT_MENU);
428 if (!menu)
429 return log_msg_ret("find", -ENOENT);
430
431 /* Check that the text ID is valid */
Simon Glassd353b752023-06-01 10:22:55 -0600432 if (!scene_obj_find(scn, label_id, SCENEOBJT_TEXT))
Simon Glass9f513932023-01-06 08:52:38 -0600433 return log_msg_ret("txt", -EINVAL);
434
Dan Carpenter463be542023-07-31 17:08:29 +0300435 item = calloc(1, sizeof(struct scene_menitem));
Simon Glass9f513932023-01-06 08:52:38 -0600436 if (!item)
437 return log_msg_ret("item", -ENOMEM);
438 item->name = strdup(name);
439 if (!item->name) {
440 free(item);
441 return log_msg_ret("name", -ENOMEM);
442 }
443
444 item->id = resolve_id(scn->expo, id);
445 item->key_id = key_id;
446 item->label_id = label_id;
447 item->desc_id = desc_id;
448 item->preview_id = preview_id;
449 item->flags = flags;
450 list_add_tail(&item->sibling, &menu->item_head);
451
Simon Glass9f513932023-01-06 08:52:38 -0600452 if (itemp)
453 *itemp = item;
454
455 return item->id;
456}
457
458int scene_menu_set_title(struct scene *scn, uint id, uint title_id)
459{
460 struct scene_obj_menu *menu;
461 struct scene_obj_txt *txt;
462
463 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
464 if (!menu)
465 return log_msg_ret("menu", -ENOENT);
466
467 /* Check that the ID is valid */
468 if (title_id) {
469 txt = scene_obj_find(scn, title_id, SCENEOBJT_TEXT);
470 if (!txt)
471 return log_msg_ret("txt", -EINVAL);
472 }
473
474 menu->title_id = title_id;
475
476 return 0;
477}
478
479int scene_menu_set_pointer(struct scene *scn, uint id, uint pointer_id)
480{
481 struct scene_obj_menu *menu;
482 struct scene_obj *obj;
483
484 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
485 if (!menu)
486 return log_msg_ret("menu", -ENOENT);
487
488 /* Check that the ID is valid */
489 if (pointer_id) {
490 obj = scene_obj_find(scn, pointer_id, SCENEOBJT_NONE);
491 if (!obj)
492 return log_msg_ret("obj", -EINVAL);
493 }
494
495 menu->pointer_id = pointer_id;
496
497 return 0;
498}
499
500int scene_menu_display(struct scene_obj_menu *menu)
501{
502 struct scene *scn = menu->obj.scene;
503 struct scene_obj_txt *pointer;
504 struct expo *exp = scn->expo;
505 struct scene_menitem *item;
506 const char *pstr;
507
508 printf("U-Boot : Boot Menu\n\n");
509 if (menu->title_id) {
510 struct scene_obj_txt *txt;
511 const char *str;
512
513 txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_TEXT);
514 if (!txt)
515 return log_msg_ret("txt", -EINVAL);
516
517 str = expo_get_str(exp, txt->str_id);
518 printf("%s\n\n", str);
519 }
520
521 if (list_empty(&menu->item_head))
522 return 0;
523
524 pointer = scene_obj_find(scn, menu->pointer_id, SCENEOBJT_TEXT);
525 pstr = expo_get_str(scn->expo, pointer->str_id);
526
527 list_for_each_entry(item, &menu->item_head, sibling) {
528 struct scene_obj_txt *key = NULL, *label = NULL;
529 struct scene_obj_txt *desc = NULL;
530 const char *kstr = NULL, *lstr = NULL, *dstr = NULL;
531
532 key = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
533 if (key)
534 kstr = expo_get_str(exp, key->str_id);
535
536 label = scene_obj_find(scn, item->label_id, SCENEOBJT_TEXT);
537 if (label)
538 lstr = expo_get_str(exp, label->str_id);
539
540 desc = scene_obj_find(scn, item->desc_id, SCENEOBJT_TEXT);
541 if (desc)
542 dstr = expo_get_str(exp, desc->str_id);
543
544 printf("%3s %3s %-10s %s\n",
545 pointer && menu->cur_item_id == item->id ? pstr : "",
546 kstr, lstr, dstr);
547 }
548
549 return -ENOTSUPP;
550}
Simon Glass01922ec2023-06-01 10:22:57 -0600551
552void scene_menu_render(struct scene_obj_menu *menu)
553{
554 struct expo *exp = menu->obj.scene->expo;
555 const struct expo_theme *theme = &exp->theme;
556 struct vidconsole_bbox bbox, label_bbox;
557 struct udevice *dev = exp->display;
558 struct video_priv *vid_priv;
559 struct udevice *cons = exp->cons;
560 struct vidconsole_colour old;
561 enum colour_idx fore, back;
562
563 if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
564 fore = VID_BLACK;
565 back = VID_WHITE;
566 } else {
567 fore = VID_LIGHT_GRAY;
568 back = VID_BLACK;
569 }
570
571 scene_menu_calc_bbox(menu, &bbox, &label_bbox);
572 vidconsole_push_colour(cons, fore, back, &old);
573 vid_priv = dev_get_uclass_priv(dev);
574 video_fill_part(dev, label_bbox.x0 - theme->menu_inset,
575 label_bbox.y0 - theme->menu_inset,
576 label_bbox.x1, label_bbox.y1 + theme->menu_inset,
577 vid_priv->colour_fg);
578 vidconsole_pop_colour(cons, &old);
579}
Simon Glass12f57732023-06-01 10:22:58 -0600580
581int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu)
582{
583 struct scene_menitem *item;
584
585 scene_render_deps(scn, menu->title_id);
586 scene_render_deps(scn, menu->cur_item_id);
587 scene_render_deps(scn, menu->pointer_id);
588
589 list_for_each_entry(item, &menu->item_head, sibling) {
590 scene_render_deps(scn, item->key_id);
591 scene_render_deps(scn, item->label_id);
592 scene_render_deps(scn, item->desc_id);
593 }
594
595 return 0;
596}