blob: dbc6793e30251313c88264f126d5c38539e88a6a [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
Simon Glass9f513932023-01-06 08:52:38 -060011#include <dm.h>
12#include <expo.h>
13#include <malloc.h>
14#include <mapmem.h>
15#include <menu.h>
16#include <video.h>
17#include <video_console.h>
18#include <linux/input.h>
19#include "scene_internal.h"
20
21static void scene_menuitem_destroy(struct scene_menitem *item)
22{
23 free(item->name);
24 free(item);
25}
26
27void scene_menu_destroy(struct scene_obj_menu *menu)
28{
29 struct scene_menitem *item, *next;
30
31 list_for_each_entry_safe(item, next, &menu->item_head, sibling)
32 scene_menuitem_destroy(item);
33}
34
Simon Glass5fd4f782023-08-14 16:40:32 -060035struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu,
36 int id)
Simon Glassc55eeba2023-06-01 10:22:54 -060037{
38 struct scene_menitem *item;
39
40 list_for_each_entry(item, &menu->item_head, sibling) {
41 if (item->id == id)
42 return item;
43 }
44
45 return NULL;
46}
47
Simon Glass4462fa32023-08-14 16:40:38 -060048struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu,
49 uint seq)
50{
51 struct scene_menitem *item;
52 uint i;
53
54 i = 0;
55 list_for_each_entry(item, &menu->item_head, sibling) {
56 if (i == seq)
57 return item;
58 i++;
59 }
60
61 return NULL;
62}
63
Simon Glass100389f2024-10-14 16:31:58 -060064struct scene_menitem *scene_menuitem_find_val(const struct scene_obj_menu *menu,
65 int val)
66{
67 struct scene_menitem *item;
68 uint i;
69
70 i = 0;
71 list_for_each_entry(item, &menu->item_head, sibling) {
Simon Glass6f3e87a2024-10-14 16:32:00 -060072 if (item->value == INT_MAX ? val == i : item->value == val)
Simon Glass100389f2024-10-14 16:31:58 -060073 return item;
74 i++;
75 }
76
77 return NULL;
78}
79
Simon Glassc55eeba2023-06-01 10:22:54 -060080/**
81 * update_pointers() - Update the pointer object and handle highlights
82 *
83 * @menu: Menu to update
84 * @id: ID of menu item to select/deselect
85 * @point: true if @id is being selected, false if it is being deselected
86 */
87static int update_pointers(struct scene_obj_menu *menu, uint id, bool point)
88{
89 struct scene *scn = menu->obj.scene;
Simon Glassd353b752023-06-01 10:22:55 -060090 const bool stack = scn->expo->popup;
Simon Glassc55eeba2023-06-01 10:22:54 -060091 const struct scene_menitem *item;
92 int ret;
93
94 item = scene_menuitem_find(menu, id);
95 if (!item)
96 return log_msg_ret("itm", -ENOENT);
97
98 /* adjust the pointer object to point to the selected item */
99 if (menu->pointer_id && item && point) {
100 struct scene_obj *label;
101
102 label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
103
104 ret = scene_obj_set_pos(scn, menu->pointer_id,
Simon Glassbc3a15f2025-05-02 08:46:31 -0600105 menu->obj.bbox.x0 + 200, label->bbox.y0);
Simon Glassc55eeba2023-06-01 10:22:54 -0600106 if (ret < 0)
107 return log_msg_ret("ptr", ret);
108 }
109
Simon Glassd353b752023-06-01 10:22:55 -0600110 if (stack) {
111 point &= scn->highlight_id == menu->obj.id;
112 scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT,
113 point ? SCENEOF_POINT : 0);
114 }
115
Simon Glassc55eeba2023-06-01 10:22:54 -0600116 return 0;
117}
118
Simon Glass9f513932023-01-06 08:52:38 -0600119/**
120 * menu_point_to_item() - Point to a particular menu item
121 *
122 * Sets the currently pointed-to / highlighted menu item
123 */
Simon Glassbe04b962025-05-02 08:46:24 -0600124static int menu_point_to_item(struct scene_obj_menu *menu, uint item_id)
Simon Glass9f513932023-01-06 08:52:38 -0600125{
Simon Glassbe04b962025-05-02 08:46:24 -0600126 int ret;
127
128 if (menu->cur_item_id) {
129 ret = update_pointers(menu, menu->cur_item_id, false);
130 if (ret)
131 return log_msg_ret("mpi", ret);
132 }
Simon Glass9f513932023-01-06 08:52:38 -0600133 menu->cur_item_id = item_id;
Simon Glassbe04b962025-05-02 08:46:24 -0600134 ret = update_pointers(menu, item_id, true);
135 if (ret)
136 return log_msg_ret("mpu", ret);
137
138 return 0;
Simon Glass9f513932023-01-06 08:52:38 -0600139}
140
Simon Glassf0994692023-10-01 19:13:29 -0600141void scene_menu_calc_bbox(struct scene_obj_menu *menu,
142 struct vidconsole_bbox *bbox,
143 struct vidconsole_bbox *label_bbox)
Simon Glass7a960052023-06-01 10:22:52 -0600144{
Simon Glass86f1ac52023-06-01 10:23:00 -0600145 const struct expo_theme *theme = &menu->obj.scene->expo->theme;
Simon Glass7a960052023-06-01 10:22:52 -0600146 const struct scene_menitem *item;
147
148 bbox->valid = false;
Simon Glass86f1ac52023-06-01 10:23:00 -0600149 scene_bbox_union(menu->obj.scene, menu->title_id, 0, bbox);
Simon Glass7a960052023-06-01 10:22:52 -0600150
151 label_bbox->valid = false;
152
153 list_for_each_entry(item, &menu->item_head, sibling) {
Simon Glass86f1ac52023-06-01 10:23:00 -0600154 scene_bbox_union(menu->obj.scene, item->label_id,
155 theme->menu_inset, bbox);
156 scene_bbox_union(menu->obj.scene, item->key_id, 0, bbox);
157 scene_bbox_union(menu->obj.scene, item->desc_id, 0, bbox);
158 scene_bbox_union(menu->obj.scene, item->preview_id, 0, bbox);
Simon Glass7a960052023-06-01 10:22:52 -0600159
160 /* Get the bounding box of all labels */
Simon Glass86f1ac52023-06-01 10:23:00 -0600161 scene_bbox_union(menu->obj.scene, item->label_id,
162 theme->menu_inset, label_bbox);
Simon Glass7a960052023-06-01 10:22:52 -0600163 }
Simon Glass86f1ac52023-06-01 10:23:00 -0600164
165 /*
166 * subtract the final menuitem's gap to keep the insert the same top
167 * and bottom
168 */
169 label_bbox->y1 -= theme->menuitem_gap_y;
Simon Glass7a960052023-06-01 10:22:52 -0600170}
171
172int scene_menu_calc_dims(struct scene_obj_menu *menu)
173{
174 struct vidconsole_bbox bbox, label_bbox;
175 const struct scene_menitem *item;
176
177 scene_menu_calc_bbox(menu, &bbox, &label_bbox);
178
179 /* Make all labels the same size */
180 if (label_bbox.valid) {
181 list_for_each_entry(item, &menu->item_head, sibling) {
182 scene_obj_set_size(menu->obj.scene, item->label_id,
183 label_bbox.x1 - label_bbox.x0,
184 label_bbox.y1 - label_bbox.y0);
185 }
186 }
187
188 if (bbox.valid) {
Simon Glassebec4972025-05-02 08:46:33 -0600189 menu->obj.dims.x = bbox.x1 - bbox.x0;
190 menu->obj.dims.y = bbox.y1 - bbox.y0;
Simon Glass7a960052023-06-01 10:22:52 -0600191 }
192
193 return 0;
194}
195
Simon Glass377f18e2024-10-14 16:31:55 -0600196int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr,
197 struct scene_obj_menu *menu)
Simon Glass9f513932023-01-06 08:52:38 -0600198{
Simon Glassd353b752023-06-01 10:22:55 -0600199 const bool open = menu->obj.flags & SCENEOF_OPEN;
200 struct expo *exp = scn->expo;
201 const bool stack = exp->popup;
Simon Glass86f1ac52023-06-01 10:23:00 -0600202 const struct expo_theme *theme = &exp->theme;
Simon Glass9f513932023-01-06 08:52:38 -0600203 struct scene_menitem *item;
Simon Glassc55eeba2023-06-01 10:22:54 -0600204 uint sel_id;
Simon Glassd353b752023-06-01 10:22:55 -0600205 int x, y;
Simon Glass9f513932023-01-06 08:52:38 -0600206 int ret;
207
Simon Glassbc3a15f2025-05-02 08:46:31 -0600208 x = menu->obj.bbox.x0;
209 y = menu->obj.bbox.y0;
Simon Glass9f513932023-01-06 08:52:38 -0600210 if (menu->title_id) {
Simon Glass377f18e2024-10-14 16:31:55 -0600211 int width;
212
Simon Glassbc3a15f2025-05-02 08:46:31 -0600213 ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.bbox.x0, y);
Simon Glass9f513932023-01-06 08:52:38 -0600214 if (ret < 0)
215 return log_msg_ret("tit", ret);
216
Simon Glass377f18e2024-10-14 16:31:55 -0600217 ret = scene_obj_get_hw(scn, menu->title_id, &width);
Simon Glass9f513932023-01-06 08:52:38 -0600218 if (ret < 0)
219 return log_msg_ret("hei", ret);
220
Simon Glassd353b752023-06-01 10:22:55 -0600221 if (stack)
Simon Glass377f18e2024-10-14 16:31:55 -0600222 x += arr->label_width + theme->menu_title_margin_x;
Simon Glassd353b752023-06-01 10:22:55 -0600223 else
224 y += ret * 2;
Simon Glass9f513932023-01-06 08:52:38 -0600225 }
226
227 /*
228 * Currently everything is hard-coded to particular columns so this
229 * won't work on small displays and looks strange if the font size is
230 * small. This can be updated once text measuring is supported in
231 * vidconsole
232 */
Simon Glassc55eeba2023-06-01 10:22:54 -0600233 sel_id = menu->cur_item_id;
Simon Glass9f513932023-01-06 08:52:38 -0600234 list_for_each_entry(item, &menu->item_head, sibling) {
Simon Glassd353b752023-06-01 10:22:55 -0600235 bool selected;
Simon Glass9f513932023-01-06 08:52:38 -0600236 int height;
237
Simon Glassd353b752023-06-01 10:22:55 -0600238 ret = scene_obj_get_hw(scn, item->label_id, NULL);
Simon Glass9f513932023-01-06 08:52:38 -0600239 if (ret < 0)
240 return log_msg_ret("get", ret);
241 height = ret;
242
243 if (item->flags & SCENEMIF_GAP_BEFORE)
244 y += height;
245
246 /* select an item if not done already */
Simon Glassc55eeba2023-06-01 10:22:54 -0600247 if (!sel_id)
248 sel_id = item->id;
Simon Glass9f513932023-01-06 08:52:38 -0600249
Simon Glassd353b752023-06-01 10:22:55 -0600250 selected = sel_id == item->id;
251
Simon Glass9f513932023-01-06 08:52:38 -0600252 /*
253 * Put the label on the left, then leave a space for the
254 * pointer, then the key and the description
255 */
Simon Glass86f1ac52023-06-01 10:23:00 -0600256 ret = scene_obj_set_pos(scn, item->label_id,
257 x + theme->menu_inset, y);
Simon Glass9f513932023-01-06 08:52:38 -0600258 if (ret < 0)
Simon Glassd353b752023-06-01 10:22:55 -0600259 return log_msg_ret("nam", ret);
260 scene_obj_set_hide(scn, item->label_id,
261 stack && !open && !selected);
Simon Glass9f513932023-01-06 08:52:38 -0600262
Simon Glassd353b752023-06-01 10:22:55 -0600263 if (item->key_id) {
264 ret = scene_obj_set_pos(scn, item->key_id, x + 230, y);
265 if (ret < 0)
266 return log_msg_ret("key", ret);
267 }
Simon Glass9f513932023-01-06 08:52:38 -0600268
Simon Glassd353b752023-06-01 10:22:55 -0600269 if (item->desc_id) {
270 ret = scene_obj_set_pos(scn, item->desc_id, x + 280, y);
271 if (ret < 0)
272 return log_msg_ret("des", ret);
273 }
Simon Glass9f513932023-01-06 08:52:38 -0600274
275 if (item->preview_id) {
276 bool hide;
277
278 /*
279 * put all previews on top of each other, on the right
280 * size of the display
281 */
282 ret = scene_obj_set_pos(scn, item->preview_id, -4, y);
283 if (ret < 0)
284 return log_msg_ret("prev", ret);
285
286 hide = menu->cur_item_id != item->id;
287 ret = scene_obj_set_hide(scn, item->preview_id, hide);
288 if (ret < 0)
289 return log_msg_ret("hid", ret);
290 }
291
Simon Glassd353b752023-06-01 10:22:55 -0600292 if (!stack || open)
Simon Glass86f1ac52023-06-01 10:23:00 -0600293 y += height + theme->menuitem_gap_y;
Simon Glass9f513932023-01-06 08:52:38 -0600294 }
295
Simon Glassc55eeba2023-06-01 10:22:54 -0600296 if (sel_id)
297 menu_point_to_item(menu, sel_id);
Simon Glassebec4972025-05-02 08:46:33 -0600298 menu->obj.bbox.x1 = menu->obj.bbox.x0 + menu->obj.dims.x;
299 menu->obj.bbox.y1 = menu->obj.bbox.y0 + menu->obj.dims.y;
300 menu->obj.flags |= SCENEOF_SIZE_VALID;
Simon Glass9f513932023-01-06 08:52:38 -0600301
302 return 0;
303}
304
305int scene_menu(struct scene *scn, const char *name, uint id,
306 struct scene_obj_menu **menup)
307{
308 struct scene_obj_menu *menu;
309 int ret;
310
311 ret = scene_obj_add(scn, name, id, SCENEOBJT_MENU,
312 sizeof(struct scene_obj_menu),
313 (struct scene_obj **)&menu);
314 if (ret < 0)
315 return log_msg_ret("obj", -ENOMEM);
316
317 if (menup)
318 *menup = menu;
319 INIT_LIST_HEAD(&menu->item_head);
320
Simon Glass9f513932023-01-06 08:52:38 -0600321 return menu->obj.id;
322}
323
324static struct scene_menitem *scene_menu_find_key(struct scene *scn,
325 struct scene_obj_menu *menu,
326 int key)
327{
328 struct scene_menitem *item;
329
330 list_for_each_entry(item, &menu->item_head, sibling) {
331 if (item->key_id) {
332 struct scene_obj_txt *txt;
333 const char *str;
334
335 txt = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
336 if (txt) {
337 str = expo_get_str(scn->expo, txt->str_id);
338 if (str && *str == key)
339 return item;
340 }
341 }
342 }
343
344 return NULL;
345}
346
347int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
348 struct expo_action *event)
349{
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600350 const bool open = menu->obj.flags & SCENEOF_OPEN;
Simon Glass9f513932023-01-06 08:52:38 -0600351 struct scene_menitem *item, *cur, *key_item;
352
353 cur = NULL;
354 key_item = NULL;
355
356 if (!list_empty(&menu->item_head)) {
357 list_for_each_entry(item, &menu->item_head, sibling) {
358 /* select an item if not done already */
359 if (menu->cur_item_id == item->id) {
360 cur = item;
361 break;
362 }
363 }
364 }
365
366 if (!cur)
367 return -ENOTTY;
368
369 switch (key) {
370 case BKEY_UP:
371 if (item != list_first_entry(&menu->item_head,
372 struct scene_menitem, sibling)) {
373 item = list_entry(item->sibling.prev,
374 struct scene_menitem, sibling);
Simon Glass719a3c62023-06-01 10:22:56 -0600375 event->type = EXPOACT_POINT_ITEM;
Simon Glass9f513932023-01-06 08:52:38 -0600376 event->select.id = item->id;
377 log_debug("up to item %d\n", event->select.id);
378 }
379 break;
380 case BKEY_DOWN:
381 if (!list_is_last(&item->sibling, &menu->item_head)) {
382 item = list_entry(item->sibling.next,
383 struct scene_menitem, sibling);
Simon Glass719a3c62023-06-01 10:22:56 -0600384 event->type = EXPOACT_POINT_ITEM;
Simon Glass9f513932023-01-06 08:52:38 -0600385 event->select.id = item->id;
386 log_debug("down to item %d\n", event->select.id);
387 }
388 break;
389 case BKEY_SELECT:
390 event->type = EXPOACT_SELECT;
391 event->select.id = item->id;
392 log_debug("select item %d\n", event->select.id);
393 break;
394 case BKEY_QUIT:
Simon Glassf0e1e8c2023-06-01 10:22:59 -0600395 if (scn->expo->popup && open) {
396 event->type = EXPOACT_CLOSE;
397 event->select.id = menu->obj.id;
398 } else {
399 event->type = EXPOACT_QUIT;
400 log_debug("menu quit\n");
401 }
Simon Glass9f513932023-01-06 08:52:38 -0600402 break;
403 case '0'...'9':
404 key_item = scene_menu_find_key(scn, menu, key);
405 if (key_item) {
406 event->type = EXPOACT_SELECT;
407 event->select.id = key_item->id;
408 }
409 break;
410 }
411
Simon Glass9f513932023-01-06 08:52:38 -0600412 return 0;
413}
414
415int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
416 uint key_id, uint label_id, uint desc_id, uint preview_id,
417 uint flags, struct scene_menitem **itemp)
418{
419 struct scene_obj_menu *menu;
420 struct scene_menitem *item;
Simon Glass9f513932023-01-06 08:52:38 -0600421
422 menu = scene_obj_find(scn, menu_id, SCENEOBJT_MENU);
423 if (!menu)
424 return log_msg_ret("find", -ENOENT);
425
426 /* Check that the text ID is valid */
Simon Glassd353b752023-06-01 10:22:55 -0600427 if (!scene_obj_find(scn, label_id, SCENEOBJT_TEXT))
Simon Glass9f513932023-01-06 08:52:38 -0600428 return log_msg_ret("txt", -EINVAL);
429
Dan Carpenter463be542023-07-31 17:08:29 +0300430 item = calloc(1, sizeof(struct scene_menitem));
Simon Glass9f513932023-01-06 08:52:38 -0600431 if (!item)
432 return log_msg_ret("item", -ENOMEM);
433 item->name = strdup(name);
434 if (!item->name) {
435 free(item);
436 return log_msg_ret("name", -ENOMEM);
437 }
438
439 item->id = resolve_id(scn->expo, id);
440 item->key_id = key_id;
441 item->label_id = label_id;
442 item->desc_id = desc_id;
443 item->preview_id = preview_id;
444 item->flags = flags;
Simon Glass100389f2024-10-14 16:31:58 -0600445 item->value = INT_MAX;
Simon Glass9f513932023-01-06 08:52:38 -0600446 list_add_tail(&item->sibling, &menu->item_head);
447
Simon Glass9f513932023-01-06 08:52:38 -0600448 if (itemp)
449 *itemp = item;
450
451 return item->id;
452}
453
454int scene_menu_set_title(struct scene *scn, uint id, uint title_id)
455{
456 struct scene_obj_menu *menu;
457 struct scene_obj_txt *txt;
458
459 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
460 if (!menu)
461 return log_msg_ret("menu", -ENOENT);
462
463 /* Check that the ID is valid */
464 if (title_id) {
465 txt = scene_obj_find(scn, title_id, SCENEOBJT_TEXT);
466 if (!txt)
467 return log_msg_ret("txt", -EINVAL);
468 }
469
470 menu->title_id = title_id;
471
472 return 0;
473}
474
475int scene_menu_set_pointer(struct scene *scn, uint id, uint pointer_id)
476{
477 struct scene_obj_menu *menu;
478 struct scene_obj *obj;
479
480 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
481 if (!menu)
482 return log_msg_ret("menu", -ENOENT);
483
484 /* Check that the ID is valid */
485 if (pointer_id) {
486 obj = scene_obj_find(scn, pointer_id, SCENEOBJT_NONE);
487 if (!obj)
488 return log_msg_ret("obj", -EINVAL);
489 }
490
491 menu->pointer_id = pointer_id;
492
493 return 0;
494}
495
Simon Glassbe04b962025-05-02 08:46:24 -0600496int scene_menu_select_item(struct scene *scn, uint id, uint cur_item_id)
497{
498 struct scene_obj_menu *menu;
499 int ret;
500
501 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
502 if (!menu)
503 return log_msg_ret("menu", -ENOENT);
504
505 ret = menu_point_to_item(menu, cur_item_id);
506 if (ret)
507 return log_msg_ret("msi", ret);
508
509 return 0;
510}
511
512int scene_menu_get_cur_item(struct scene *scn, uint id)
513{
514 struct scene_obj_menu *menu;
515
516 menu = scene_obj_find(scn, id, SCENEOBJT_MENU);
517 if (!menu)
518 return log_msg_ret("menu", -ENOENT);
519
520 return menu->cur_item_id;
521}
522
Simon Glass9f513932023-01-06 08:52:38 -0600523int scene_menu_display(struct scene_obj_menu *menu)
524{
525 struct scene *scn = menu->obj.scene;
526 struct scene_obj_txt *pointer;
527 struct expo *exp = scn->expo;
528 struct scene_menitem *item;
529 const char *pstr;
530
531 printf("U-Boot : Boot Menu\n\n");
532 if (menu->title_id) {
533 struct scene_obj_txt *txt;
534 const char *str;
535
536 txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_TEXT);
537 if (!txt)
538 return log_msg_ret("txt", -EINVAL);
539
540 str = expo_get_str(exp, txt->str_id);
541 printf("%s\n\n", str);
542 }
543
544 if (list_empty(&menu->item_head))
545 return 0;
546
547 pointer = scene_obj_find(scn, menu->pointer_id, SCENEOBJT_TEXT);
548 pstr = expo_get_str(scn->expo, pointer->str_id);
549
550 list_for_each_entry(item, &menu->item_head, sibling) {
551 struct scene_obj_txt *key = NULL, *label = NULL;
552 struct scene_obj_txt *desc = NULL;
553 const char *kstr = NULL, *lstr = NULL, *dstr = NULL;
554
555 key = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT);
556 if (key)
557 kstr = expo_get_str(exp, key->str_id);
558
559 label = scene_obj_find(scn, item->label_id, SCENEOBJT_TEXT);
560 if (label)
561 lstr = expo_get_str(exp, label->str_id);
562
563 desc = scene_obj_find(scn, item->desc_id, SCENEOBJT_TEXT);
564 if (desc)
565 dstr = expo_get_str(exp, desc->str_id);
566
567 printf("%3s %3s %-10s %s\n",
568 pointer && menu->cur_item_id == item->id ? pstr : "",
569 kstr, lstr, dstr);
570 }
571
572 return -ENOTSUPP;
573}
Simon Glass01922ec2023-06-01 10:22:57 -0600574
Simon Glass12f57732023-06-01 10:22:58 -0600575int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu)
576{
577 struct scene_menitem *item;
578
579 scene_render_deps(scn, menu->title_id);
580 scene_render_deps(scn, menu->cur_item_id);
581 scene_render_deps(scn, menu->pointer_id);
582
583 list_for_each_entry(item, &menu->item_head, sibling) {
584 scene_render_deps(scn, item->key_id);
585 scene_render_deps(scn, item->label_id);
586 scene_render_deps(scn, item->desc_id);
587 }
588
589 return 0;
590}