]>
Commit | Line | Data |
---|---|---|
226777f1 SG |
1 | // 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 <[email protected]> | |
7 | */ | |
8 | ||
c98cb512 | 9 | #define LOG_CATEGORY LOGC_EXPO |
226777f1 SG |
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 | ||
22 | static void scene_menuitem_destroy(struct scene_menitem *item) | |
23 | { | |
24 | free(item->name); | |
25 | free(item); | |
26 | } | |
27 | ||
28 | void 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 | ||
8872bc7f SG |
36 | static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu, |
37 | int id) | |
38 | { | |
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 | ||
49 | /** | |
50 | * update_pointers() - Update the pointer object and handle highlights | |
51 | * | |
52 | * @menu: Menu to update | |
53 | * @id: ID of menu item to select/deselect | |
54 | * @point: true if @id is being selected, false if it is being deselected | |
55 | */ | |
56 | static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) | |
57 | { | |
58 | struct scene *scn = menu->obj.scene; | |
d3db0216 | 59 | const bool stack = scn->expo->popup; |
8872bc7f SG |
60 | const struct scene_menitem *item; |
61 | int ret; | |
62 | ||
63 | item = scene_menuitem_find(menu, id); | |
64 | if (!item) | |
65 | return log_msg_ret("itm", -ENOENT); | |
66 | ||
67 | /* adjust the pointer object to point to the selected item */ | |
68 | if (menu->pointer_id && item && point) { | |
69 | struct scene_obj *label; | |
70 | ||
71 | label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE); | |
72 | ||
73 | ret = scene_obj_set_pos(scn, menu->pointer_id, | |
74 | menu->obj.dim.x + 200, label->dim.y); | |
75 | if (ret < 0) | |
76 | return log_msg_ret("ptr", ret); | |
77 | } | |
78 | ||
d3db0216 SG |
79 | if (stack) { |
80 | point &= scn->highlight_id == menu->obj.id; | |
81 | scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT, | |
82 | point ? SCENEOF_POINT : 0); | |
83 | } | |
84 | ||
8872bc7f SG |
85 | return 0; |
86 | } | |
87 | ||
226777f1 SG |
88 | /** |
89 | * menu_point_to_item() - Point to a particular menu item | |
90 | * | |
91 | * Sets the currently pointed-to / highlighted menu item | |
92 | */ | |
93 | static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id) | |
94 | { | |
8872bc7f SG |
95 | if (menu->cur_item_id) |
96 | update_pointers(menu, menu->cur_item_id, false); | |
226777f1 | 97 | menu->cur_item_id = item_id; |
8872bc7f | 98 | update_pointers(menu, item_id, true); |
226777f1 SG |
99 | } |
100 | ||
7230fdb3 | 101 | static int scene_bbox_union(struct scene *scn, uint id, int inset, |
699b0acb SG |
102 | struct vidconsole_bbox *bbox) |
103 | { | |
104 | struct scene_obj *obj; | |
105 | ||
106 | if (!id) | |
107 | return 0; | |
108 | obj = scene_obj_find(scn, id, SCENEOBJT_NONE); | |
109 | if (!obj) | |
110 | return log_msg_ret("obj", -ENOENT); | |
111 | if (bbox->valid) { | |
7230fdb3 | 112 | bbox->x0 = min(bbox->x0, obj->dim.x - inset); |
699b0acb | 113 | bbox->y0 = min(bbox->y0, obj->dim.y); |
7230fdb3 | 114 | bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset); |
699b0acb SG |
115 | bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h); |
116 | } else { | |
7230fdb3 | 117 | bbox->x0 = obj->dim.x - inset; |
699b0acb | 118 | bbox->y0 = obj->dim.y; |
7230fdb3 | 119 | bbox->x1 = obj->dim.x + obj->dim.w + inset; |
699b0acb SG |
120 | bbox->y1 = obj->dim.y + obj->dim.h; |
121 | bbox->valid = true; | |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | /** | |
128 | * scene_menu_calc_bbox() - Calculate bounding boxes for the menu | |
129 | * | |
130 | * @menu: Menu to process | |
131 | * @bbox: Returns bounding box of menu including prompts | |
132 | * @label_bbox: Returns bounding box of labels | |
133 | */ | |
134 | static void scene_menu_calc_bbox(struct scene_obj_menu *menu, | |
135 | struct vidconsole_bbox *bbox, | |
136 | struct vidconsole_bbox *label_bbox) | |
137 | { | |
7230fdb3 | 138 | const struct expo_theme *theme = &menu->obj.scene->expo->theme; |
699b0acb SG |
139 | const struct scene_menitem *item; |
140 | ||
141 | bbox->valid = false; | |
7230fdb3 | 142 | scene_bbox_union(menu->obj.scene, menu->title_id, 0, bbox); |
699b0acb SG |
143 | |
144 | label_bbox->valid = false; | |
145 | ||
146 | list_for_each_entry(item, &menu->item_head, sibling) { | |
7230fdb3 SG |
147 | scene_bbox_union(menu->obj.scene, item->label_id, |
148 | theme->menu_inset, bbox); | |
149 | scene_bbox_union(menu->obj.scene, item->key_id, 0, bbox); | |
150 | scene_bbox_union(menu->obj.scene, item->desc_id, 0, bbox); | |
151 | scene_bbox_union(menu->obj.scene, item->preview_id, 0, bbox); | |
699b0acb SG |
152 | |
153 | /* Get the bounding box of all labels */ | |
7230fdb3 SG |
154 | scene_bbox_union(menu->obj.scene, item->label_id, |
155 | theme->menu_inset, label_bbox); | |
699b0acb | 156 | } |
7230fdb3 SG |
157 | |
158 | /* | |
159 | * subtract the final menuitem's gap to keep the insert the same top | |
160 | * and bottom | |
161 | */ | |
162 | label_bbox->y1 -= theme->menuitem_gap_y; | |
699b0acb SG |
163 | } |
164 | ||
165 | int scene_menu_calc_dims(struct scene_obj_menu *menu) | |
166 | { | |
167 | struct vidconsole_bbox bbox, label_bbox; | |
168 | const struct scene_menitem *item; | |
169 | ||
170 | scene_menu_calc_bbox(menu, &bbox, &label_bbox); | |
171 | ||
172 | /* Make all labels the same size */ | |
173 | if (label_bbox.valid) { | |
174 | list_for_each_entry(item, &menu->item_head, sibling) { | |
175 | scene_obj_set_size(menu->obj.scene, item->label_id, | |
176 | label_bbox.x1 - label_bbox.x0, | |
177 | label_bbox.y1 - label_bbox.y0); | |
178 | } | |
179 | } | |
180 | ||
181 | if (bbox.valid) { | |
182 | menu->obj.dim.w = bbox.x1 - bbox.x0; | |
183 | menu->obj.dim.h = bbox.y1 - bbox.y0; | |
184 | } | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
226777f1 SG |
189 | int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) |
190 | { | |
d3db0216 SG |
191 | const bool open = menu->obj.flags & SCENEOF_OPEN; |
192 | struct expo *exp = scn->expo; | |
193 | const bool stack = exp->popup; | |
7230fdb3 | 194 | const struct expo_theme *theme = &exp->theme; |
226777f1 | 195 | struct scene_menitem *item; |
8872bc7f | 196 | uint sel_id; |
d3db0216 | 197 | int x, y; |
226777f1 SG |
198 | int ret; |
199 | ||
d3db0216 | 200 | x = menu->obj.dim.x; |
ae45d6cf | 201 | y = menu->obj.dim.y; |
226777f1 | 202 | if (menu->title_id) { |
ae45d6cf | 203 | ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y); |
226777f1 SG |
204 | if (ret < 0) |
205 | return log_msg_ret("tit", ret); | |
206 | ||
207 | ret = scene_obj_get_hw(scn, menu->title_id, NULL); | |
208 | if (ret < 0) | |
209 | return log_msg_ret("hei", ret); | |
210 | ||
d3db0216 SG |
211 | if (stack) |
212 | x += 200; | |
213 | else | |
214 | y += ret * 2; | |
226777f1 SG |
215 | } |
216 | ||
217 | /* | |
218 | * Currently everything is hard-coded to particular columns so this | |
219 | * won't work on small displays and looks strange if the font size is | |
220 | * small. This can be updated once text measuring is supported in | |
221 | * vidconsole | |
222 | */ | |
8872bc7f | 223 | sel_id = menu->cur_item_id; |
226777f1 | 224 | list_for_each_entry(item, &menu->item_head, sibling) { |
d3db0216 | 225 | bool selected; |
226777f1 SG |
226 | int height; |
227 | ||
d3db0216 | 228 | ret = scene_obj_get_hw(scn, item->label_id, NULL); |
226777f1 SG |
229 | if (ret < 0) |
230 | return log_msg_ret("get", ret); | |
231 | height = ret; | |
232 | ||
233 | if (item->flags & SCENEMIF_GAP_BEFORE) | |
234 | y += height; | |
235 | ||
236 | /* select an item if not done already */ | |
8872bc7f SG |
237 | if (!sel_id) |
238 | sel_id = item->id; | |
226777f1 | 239 | |
d3db0216 SG |
240 | selected = sel_id == item->id; |
241 | ||
226777f1 SG |
242 | /* |
243 | * Put the label on the left, then leave a space for the | |
244 | * pointer, then the key and the description | |
245 | */ | |
7230fdb3 SG |
246 | ret = scene_obj_set_pos(scn, item->label_id, |
247 | x + theme->menu_inset, y); | |
226777f1 | 248 | if (ret < 0) |
d3db0216 SG |
249 | return log_msg_ret("nam", ret); |
250 | scene_obj_set_hide(scn, item->label_id, | |
251 | stack && !open && !selected); | |
226777f1 | 252 | |
d3db0216 SG |
253 | if (item->key_id) { |
254 | ret = scene_obj_set_pos(scn, item->key_id, x + 230, y); | |
255 | if (ret < 0) | |
256 | return log_msg_ret("key", ret); | |
257 | } | |
226777f1 | 258 | |
d3db0216 SG |
259 | if (item->desc_id) { |
260 | ret = scene_obj_set_pos(scn, item->desc_id, x + 280, y); | |
261 | if (ret < 0) | |
262 | return log_msg_ret("des", ret); | |
263 | } | |
226777f1 SG |
264 | |
265 | if (item->preview_id) { | |
266 | bool hide; | |
267 | ||
268 | /* | |
269 | * put all previews on top of each other, on the right | |
270 | * size of the display | |
271 | */ | |
272 | ret = scene_obj_set_pos(scn, item->preview_id, -4, y); | |
273 | if (ret < 0) | |
274 | return log_msg_ret("prev", ret); | |
275 | ||
276 | hide = menu->cur_item_id != item->id; | |
277 | ret = scene_obj_set_hide(scn, item->preview_id, hide); | |
278 | if (ret < 0) | |
279 | return log_msg_ret("hid", ret); | |
280 | } | |
281 | ||
d3db0216 | 282 | if (!stack || open) |
7230fdb3 | 283 | y += height + theme->menuitem_gap_y; |
226777f1 SG |
284 | } |
285 | ||
8872bc7f SG |
286 | if (sel_id) |
287 | menu_point_to_item(menu, sel_id); | |
226777f1 SG |
288 | |
289 | return 0; | |
290 | } | |
291 | ||
292 | int scene_menu(struct scene *scn, const char *name, uint id, | |
293 | struct scene_obj_menu **menup) | |
294 | { | |
295 | struct scene_obj_menu *menu; | |
296 | int ret; | |
297 | ||
298 | ret = scene_obj_add(scn, name, id, SCENEOBJT_MENU, | |
299 | sizeof(struct scene_obj_menu), | |
300 | (struct scene_obj **)&menu); | |
301 | if (ret < 0) | |
302 | return log_msg_ret("obj", -ENOMEM); | |
303 | ||
304 | if (menup) | |
305 | *menup = menu; | |
306 | INIT_LIST_HEAD(&menu->item_head); | |
307 | ||
226777f1 SG |
308 | return menu->obj.id; |
309 | } | |
310 | ||
311 | static struct scene_menitem *scene_menu_find_key(struct scene *scn, | |
312 | struct scene_obj_menu *menu, | |
313 | int key) | |
314 | { | |
315 | struct scene_menitem *item; | |
316 | ||
317 | list_for_each_entry(item, &menu->item_head, sibling) { | |
318 | if (item->key_id) { | |
319 | struct scene_obj_txt *txt; | |
320 | const char *str; | |
321 | ||
322 | txt = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT); | |
323 | if (txt) { | |
324 | str = expo_get_str(scn->expo, txt->str_id); | |
325 | if (str && *str == key) | |
326 | return item; | |
327 | } | |
328 | } | |
329 | } | |
330 | ||
331 | return NULL; | |
332 | } | |
333 | ||
334 | int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, | |
335 | struct expo_action *event) | |
336 | { | |
4e64beeb | 337 | const bool open = menu->obj.flags & SCENEOF_OPEN; |
226777f1 SG |
338 | struct scene_menitem *item, *cur, *key_item; |
339 | ||
340 | cur = NULL; | |
341 | key_item = NULL; | |
342 | ||
343 | if (!list_empty(&menu->item_head)) { | |
344 | list_for_each_entry(item, &menu->item_head, sibling) { | |
345 | /* select an item if not done already */ | |
346 | if (menu->cur_item_id == item->id) { | |
347 | cur = item; | |
348 | break; | |
349 | } | |
350 | } | |
351 | } | |
352 | ||
353 | if (!cur) | |
354 | return -ENOTTY; | |
355 | ||
356 | switch (key) { | |
357 | case BKEY_UP: | |
358 | if (item != list_first_entry(&menu->item_head, | |
359 | struct scene_menitem, sibling)) { | |
360 | item = list_entry(item->sibling.prev, | |
361 | struct scene_menitem, sibling); | |
3f33b9c7 | 362 | event->type = EXPOACT_POINT_ITEM; |
226777f1 SG |
363 | event->select.id = item->id; |
364 | log_debug("up to item %d\n", event->select.id); | |
365 | } | |
366 | break; | |
367 | case BKEY_DOWN: | |
368 | if (!list_is_last(&item->sibling, &menu->item_head)) { | |
369 | item = list_entry(item->sibling.next, | |
370 | struct scene_menitem, sibling); | |
3f33b9c7 | 371 | event->type = EXPOACT_POINT_ITEM; |
226777f1 SG |
372 | event->select.id = item->id; |
373 | log_debug("down to item %d\n", event->select.id); | |
374 | } | |
375 | break; | |
376 | case BKEY_SELECT: | |
377 | event->type = EXPOACT_SELECT; | |
378 | event->select.id = item->id; | |
379 | log_debug("select item %d\n", event->select.id); | |
380 | break; | |
381 | case BKEY_QUIT: | |
4e64beeb SG |
382 | if (scn->expo->popup && open) { |
383 | event->type = EXPOACT_CLOSE; | |
384 | event->select.id = menu->obj.id; | |
385 | } else { | |
386 | event->type = EXPOACT_QUIT; | |
387 | log_debug("menu quit\n"); | |
388 | } | |
226777f1 SG |
389 | break; |
390 | case '0'...'9': | |
391 | key_item = scene_menu_find_key(scn, menu, key); | |
392 | if (key_item) { | |
393 | event->type = EXPOACT_SELECT; | |
394 | event->select.id = key_item->id; | |
395 | } | |
396 | break; | |
397 | } | |
398 | ||
399 | menu_point_to_item(menu, item->id); | |
400 | ||
401 | return 0; | |
402 | } | |
403 | ||
404 | int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id, | |
405 | uint key_id, uint label_id, uint desc_id, uint preview_id, | |
406 | uint flags, struct scene_menitem **itemp) | |
407 | { | |
408 | struct scene_obj_menu *menu; | |
409 | struct scene_menitem *item; | |
226777f1 SG |
410 | |
411 | menu = scene_obj_find(scn, menu_id, SCENEOBJT_MENU); | |
412 | if (!menu) | |
413 | return log_msg_ret("find", -ENOENT); | |
414 | ||
415 | /* Check that the text ID is valid */ | |
d3db0216 | 416 | if (!scene_obj_find(scn, label_id, SCENEOBJT_TEXT)) |
226777f1 SG |
417 | return log_msg_ret("txt", -EINVAL); |
418 | ||
419 | item = calloc(1, sizeof(struct scene_obj_menu)); | |
420 | if (!item) | |
421 | return log_msg_ret("item", -ENOMEM); | |
422 | item->name = strdup(name); | |
423 | if (!item->name) { | |
424 | free(item); | |
425 | return log_msg_ret("name", -ENOMEM); | |
426 | } | |
427 | ||
428 | item->id = resolve_id(scn->expo, id); | |
429 | item->key_id = key_id; | |
430 | item->label_id = label_id; | |
431 | item->desc_id = desc_id; | |
432 | item->preview_id = preview_id; | |
433 | item->flags = flags; | |
434 | list_add_tail(&item->sibling, &menu->item_head); | |
435 | ||
226777f1 SG |
436 | if (itemp) |
437 | *itemp = item; | |
438 | ||
439 | return item->id; | |
440 | } | |
441 | ||
442 | int scene_menu_set_title(struct scene *scn, uint id, uint title_id) | |
443 | { | |
444 | struct scene_obj_menu *menu; | |
445 | struct scene_obj_txt *txt; | |
446 | ||
447 | menu = scene_obj_find(scn, id, SCENEOBJT_MENU); | |
448 | if (!menu) | |
449 | return log_msg_ret("menu", -ENOENT); | |
450 | ||
451 | /* Check that the ID is valid */ | |
452 | if (title_id) { | |
453 | txt = scene_obj_find(scn, title_id, SCENEOBJT_TEXT); | |
454 | if (!txt) | |
455 | return log_msg_ret("txt", -EINVAL); | |
456 | } | |
457 | ||
458 | menu->title_id = title_id; | |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
463 | int scene_menu_set_pointer(struct scene *scn, uint id, uint pointer_id) | |
464 | { | |
465 | struct scene_obj_menu *menu; | |
466 | struct scene_obj *obj; | |
467 | ||
468 | menu = scene_obj_find(scn, id, SCENEOBJT_MENU); | |
469 | if (!menu) | |
470 | return log_msg_ret("menu", -ENOENT); | |
471 | ||
472 | /* Check that the ID is valid */ | |
473 | if (pointer_id) { | |
474 | obj = scene_obj_find(scn, pointer_id, SCENEOBJT_NONE); | |
475 | if (!obj) | |
476 | return log_msg_ret("obj", -EINVAL); | |
477 | } | |
478 | ||
479 | menu->pointer_id = pointer_id; | |
480 | ||
481 | return 0; | |
482 | } | |
483 | ||
484 | int scene_menu_display(struct scene_obj_menu *menu) | |
485 | { | |
486 | struct scene *scn = menu->obj.scene; | |
487 | struct scene_obj_txt *pointer; | |
488 | struct expo *exp = scn->expo; | |
489 | struct scene_menitem *item; | |
490 | const char *pstr; | |
491 | ||
492 | printf("U-Boot : Boot Menu\n\n"); | |
493 | if (menu->title_id) { | |
494 | struct scene_obj_txt *txt; | |
495 | const char *str; | |
496 | ||
497 | txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_TEXT); | |
498 | if (!txt) | |
499 | return log_msg_ret("txt", -EINVAL); | |
500 | ||
501 | str = expo_get_str(exp, txt->str_id); | |
502 | printf("%s\n\n", str); | |
503 | } | |
504 | ||
505 | if (list_empty(&menu->item_head)) | |
506 | return 0; | |
507 | ||
508 | pointer = scene_obj_find(scn, menu->pointer_id, SCENEOBJT_TEXT); | |
509 | pstr = expo_get_str(scn->expo, pointer->str_id); | |
510 | ||
511 | list_for_each_entry(item, &menu->item_head, sibling) { | |
512 | struct scene_obj_txt *key = NULL, *label = NULL; | |
513 | struct scene_obj_txt *desc = NULL; | |
514 | const char *kstr = NULL, *lstr = NULL, *dstr = NULL; | |
515 | ||
516 | key = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT); | |
517 | if (key) | |
518 | kstr = expo_get_str(exp, key->str_id); | |
519 | ||
520 | label = scene_obj_find(scn, item->label_id, SCENEOBJT_TEXT); | |
521 | if (label) | |
522 | lstr = expo_get_str(exp, label->str_id); | |
523 | ||
524 | desc = scene_obj_find(scn, item->desc_id, SCENEOBJT_TEXT); | |
525 | if (desc) | |
526 | dstr = expo_get_str(exp, desc->str_id); | |
527 | ||
528 | printf("%3s %3s %-10s %s\n", | |
529 | pointer && menu->cur_item_id == item->id ? pstr : "", | |
530 | kstr, lstr, dstr); | |
531 | } | |
532 | ||
533 | return -ENOTSUPP; | |
534 | } | |
756c9559 SG |
535 | |
536 | void scene_menu_render(struct scene_obj_menu *menu) | |
537 | { | |
538 | struct expo *exp = menu->obj.scene->expo; | |
539 | const struct expo_theme *theme = &exp->theme; | |
540 | struct vidconsole_bbox bbox, label_bbox; | |
541 | struct udevice *dev = exp->display; | |
542 | struct video_priv *vid_priv; | |
543 | struct udevice *cons = exp->cons; | |
544 | struct vidconsole_colour old; | |
545 | enum colour_idx fore, back; | |
546 | ||
547 | if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) { | |
548 | fore = VID_BLACK; | |
549 | back = VID_WHITE; | |
550 | } else { | |
551 | fore = VID_LIGHT_GRAY; | |
552 | back = VID_BLACK; | |
553 | } | |
554 | ||
555 | scene_menu_calc_bbox(menu, &bbox, &label_bbox); | |
556 | vidconsole_push_colour(cons, fore, back, &old); | |
557 | vid_priv = dev_get_uclass_priv(dev); | |
558 | video_fill_part(dev, label_bbox.x0 - theme->menu_inset, | |
559 | label_bbox.y0 - theme->menu_inset, | |
560 | label_bbox.x1, label_bbox.y1 + theme->menu_inset, | |
561 | vid_priv->colour_fg); | |
562 | vidconsole_pop_colour(cons, &old); | |
563 | } | |
4c87e073 SG |
564 | |
565 | int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu) | |
566 | { | |
567 | struct scene_menitem *item; | |
568 | ||
569 | scene_render_deps(scn, menu->title_id); | |
570 | scene_render_deps(scn, menu->cur_item_id); | |
571 | scene_render_deps(scn, menu->pointer_id); | |
572 | ||
573 | list_for_each_entry(item, &menu->item_head, sibling) { | |
574 | scene_render_deps(scn, item->key_id); | |
575 | scene_render_deps(scn, item->label_id); | |
576 | scene_render_deps(scn, item->desc_id); | |
577 | } | |
578 | ||
579 | return 0; | |
580 | } |