]>
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 | ||
6e648fa7 SG |
36 | struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu, |
37 | int id) | |
8872bc7f SG |
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 | ||
cfc402db SG |
49 | struct 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 | ||
8872bc7f SG |
65 | /** |
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 | */ | |
72 | static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) | |
73 | { | |
74 | struct scene *scn = menu->obj.scene; | |
d3db0216 | 75 | const bool stack = scn->expo->popup; |
8872bc7f SG |
76 | 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 | ||
d3db0216 SG |
95 | 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 | ||
8872bc7f SG |
101 | return 0; |
102 | } | |
103 | ||
226777f1 SG |
104 | /** |
105 | * menu_point_to_item() - Point to a particular menu item | |
106 | * | |
107 | * Sets the currently pointed-to / highlighted menu item | |
108 | */ | |
109 | static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id) | |
110 | { | |
8872bc7f SG |
111 | if (menu->cur_item_id) |
112 | update_pointers(menu, menu->cur_item_id, false); | |
226777f1 | 113 | menu->cur_item_id = item_id; |
8872bc7f | 114 | update_pointers(menu, item_id, true); |
226777f1 SG |
115 | } |
116 | ||
7230fdb3 | 117 | static int scene_bbox_union(struct scene *scn, uint id, int inset, |
699b0acb SG |
118 | 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) { | |
7230fdb3 | 128 | bbox->x0 = min(bbox->x0, obj->dim.x - inset); |
699b0acb | 129 | bbox->y0 = min(bbox->y0, obj->dim.y); |
7230fdb3 | 130 | bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset); |
699b0acb SG |
131 | bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h); |
132 | } else { | |
7230fdb3 | 133 | bbox->x0 = obj->dim.x - inset; |
699b0acb | 134 | bbox->y0 = obj->dim.y; |
7230fdb3 | 135 | bbox->x1 = obj->dim.x + obj->dim.w + inset; |
699b0acb SG |
136 | 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 | */ | |
150 | static void scene_menu_calc_bbox(struct scene_obj_menu *menu, | |
151 | struct vidconsole_bbox *bbox, | |
152 | struct vidconsole_bbox *label_bbox) | |
153 | { | |
7230fdb3 | 154 | const struct expo_theme *theme = &menu->obj.scene->expo->theme; |
699b0acb SG |
155 | const struct scene_menitem *item; |
156 | ||
157 | bbox->valid = false; | |
7230fdb3 | 158 | scene_bbox_union(menu->obj.scene, menu->title_id, 0, bbox); |
699b0acb SG |
159 | |
160 | label_bbox->valid = false; | |
161 | ||
162 | list_for_each_entry(item, &menu->item_head, sibling) { | |
7230fdb3 SG |
163 | 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); | |
699b0acb SG |
168 | |
169 | /* Get the bounding box of all labels */ | |
7230fdb3 SG |
170 | scene_bbox_union(menu->obj.scene, item->label_id, |
171 | theme->menu_inset, label_bbox); | |
699b0acb | 172 | } |
7230fdb3 SG |
173 | |
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; | |
699b0acb SG |
179 | } |
180 | ||
181 | int 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 | ||
226777f1 SG |
205 | int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) |
206 | { | |
d3db0216 SG |
207 | const bool open = menu->obj.flags & SCENEOF_OPEN; |
208 | struct expo *exp = scn->expo; | |
209 | const bool stack = exp->popup; | |
7230fdb3 | 210 | const struct expo_theme *theme = &exp->theme; |
226777f1 | 211 | struct scene_menitem *item; |
8872bc7f | 212 | uint sel_id; |
d3db0216 | 213 | int x, y; |
226777f1 SG |
214 | int ret; |
215 | ||
d3db0216 | 216 | x = menu->obj.dim.x; |
ae45d6cf | 217 | y = menu->obj.dim.y; |
226777f1 | 218 | if (menu->title_id) { |
ae45d6cf | 219 | ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y); |
226777f1 SG |
220 | 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 | ||
d3db0216 SG |
227 | if (stack) |
228 | x += 200; | |
229 | else | |
230 | y += ret * 2; | |
226777f1 SG |
231 | } |
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 | */ | |
8872bc7f | 239 | sel_id = menu->cur_item_id; |
226777f1 | 240 | list_for_each_entry(item, &menu->item_head, sibling) { |
d3db0216 | 241 | bool selected; |
226777f1 SG |
242 | int height; |
243 | ||
d3db0216 | 244 | ret = scene_obj_get_hw(scn, item->label_id, NULL); |
226777f1 SG |
245 | 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 */ | |
8872bc7f SG |
253 | if (!sel_id) |
254 | sel_id = item->id; | |
226777f1 | 255 | |
d3db0216 SG |
256 | selected = sel_id == item->id; |
257 | ||
226777f1 SG |
258 | /* |
259 | * Put the label on the left, then leave a space for the | |
260 | * pointer, then the key and the description | |
261 | */ | |
7230fdb3 SG |
262 | ret = scene_obj_set_pos(scn, item->label_id, |
263 | x + theme->menu_inset, y); | |
226777f1 | 264 | if (ret < 0) |
d3db0216 SG |
265 | return log_msg_ret("nam", ret); |
266 | scene_obj_set_hide(scn, item->label_id, | |
267 | stack && !open && !selected); | |
226777f1 | 268 | |
d3db0216 SG |
269 | 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 | } | |
226777f1 | 274 | |
d3db0216 SG |
275 | 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 | } | |
226777f1 SG |
280 | |
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 | ||
d3db0216 | 298 | if (!stack || open) |
7230fdb3 | 299 | y += height + theme->menuitem_gap_y; |
226777f1 SG |
300 | } |
301 | ||
8872bc7f SG |
302 | if (sel_id) |
303 | menu_point_to_item(menu, sel_id); | |
226777f1 SG |
304 | |
305 | return 0; | |
306 | } | |
307 | ||
308 | int 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 | ||
226777f1 SG |
324 | return menu->obj.id; |
325 | } | |
326 | ||
327 | static 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 | ||
350 | int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, | |
351 | struct expo_action *event) | |
352 | { | |
4e64beeb | 353 | const bool open = menu->obj.flags & SCENEOF_OPEN; |
226777f1 SG |
354 | 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); | |
3f33b9c7 | 378 | event->type = EXPOACT_POINT_ITEM; |
226777f1 SG |
379 | 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); | |
3f33b9c7 | 387 | event->type = EXPOACT_POINT_ITEM; |
226777f1 SG |
388 | 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: | |
4e64beeb SG |
398 | 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 | } | |
226777f1 SG |
405 | 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 | ||
420 | int 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; | |
226777f1 SG |
426 | |
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 */ | |
d3db0216 | 432 | if (!scene_obj_find(scn, label_id, SCENEOBJT_TEXT)) |
226777f1 SG |
433 | return log_msg_ret("txt", -EINVAL); |
434 | ||
d864bd0e | 435 | item = calloc(1, sizeof(struct scene_menitem)); |
226777f1 SG |
436 | 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 | ||
226777f1 SG |
452 | if (itemp) |
453 | *itemp = item; | |
454 | ||
455 | return item->id; | |
456 | } | |
457 | ||
458 | int 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 | ||
479 | int 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 | ||
500 | int 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 | } | |
756c9559 SG |
551 | |
552 | void 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 | } | |
4c87e073 SG |
580 | |
581 | int 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 | } |