]> Git Repo - J-u-boot.git/blob - boot/scene.c
expo: Draw popup menus in both opened and closed states
[J-u-boot.git] / boot / scene.c
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 <[email protected]>
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 <video.h>
17 #include <video_console.h>
18 #include <linux/input.h>
19 #include "scene_internal.h"
20
21 int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
22 {
23         struct scene *scn;
24
25         scn = calloc(1, sizeof(struct scene));
26         if (!scn)
27                 return log_msg_ret("expo", -ENOMEM);
28         scn->name = strdup(name);
29         if (!scn->name) {
30                 free(scn);
31                 return log_msg_ret("name", -ENOMEM);
32         }
33
34         INIT_LIST_HEAD(&scn->obj_head);
35         scn->id = resolve_id(exp, id);
36         scn->expo = exp;
37         list_add_tail(&scn->sibling, &exp->scene_head);
38
39         *scnp = scn;
40
41         return scn->id;
42 }
43
44 void scene_obj_destroy(struct scene_obj *obj)
45 {
46         if (obj->type == SCENEOBJT_MENU)
47                 scene_menu_destroy((struct scene_obj_menu *)obj);
48         free(obj->name);
49         free(obj);
50 }
51
52 void scene_destroy(struct scene *scn)
53 {
54         struct scene_obj *obj, *next;
55
56         list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
57                 scene_obj_destroy(obj);
58
59         free(scn->name);
60         free(scn);
61 }
62
63 int scene_title_set(struct scene *scn, uint id)
64 {
65         scn->title_id = id;
66
67         return 0;
68 }
69
70 int scene_obj_count(struct scene *scn)
71 {
72         struct scene_obj *obj;
73         int count = 0;
74
75         list_for_each_entry(obj, &scn->obj_head, sibling)
76                 count++;
77
78         return count;
79 }
80
81 void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type)
82 {
83         struct scene_obj *obj;
84
85         list_for_each_entry(obj, &scn->obj_head, sibling) {
86                 if (obj->id == id &&
87                     (type == SCENEOBJT_NONE || obj->type == type))
88                         return obj;
89         }
90
91         return NULL;
92 }
93
94 int scene_obj_add(struct scene *scn, const char *name, uint id,
95                   enum scene_obj_t type, uint size, struct scene_obj **objp)
96 {
97         struct scene_obj *obj;
98
99         obj = calloc(1, size);
100         if (!obj)
101                 return log_msg_ret("obj", -ENOMEM);
102         obj->name = strdup(name);
103         if (!obj->name) {
104                 free(obj);
105                 return log_msg_ret("name", -ENOMEM);
106         }
107
108         obj->id = resolve_id(scn->expo, id);
109         obj->scene = scn;
110         obj->type = type;
111         list_add_tail(&obj->sibling, &scn->obj_head);
112         *objp = obj;
113
114         return obj->id;
115 }
116
117 int scene_img(struct scene *scn, const char *name, uint id, char *data,
118               struct scene_obj_img **imgp)
119 {
120         struct scene_obj_img *img;
121         int ret;
122
123         ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
124                             sizeof(struct scene_obj_img),
125                             (struct scene_obj **)&img);
126         if (ret < 0)
127                 return log_msg_ret("obj", -ENOMEM);
128
129         img->data = data;
130
131         if (imgp)
132                 *imgp = img;
133
134         return img->obj.id;
135 }
136
137 int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
138               struct scene_obj_txt **txtp)
139 {
140         struct scene_obj_txt *txt;
141         int ret;
142
143         ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
144                             sizeof(struct scene_obj_txt),
145                             (struct scene_obj **)&txt);
146         if (ret < 0)
147                 return log_msg_ret("obj", -ENOMEM);
148
149         txt->str_id = str_id;
150
151         if (txtp)
152                 *txtp = txt;
153
154         return txt->obj.id;
155 }
156
157 int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
158                   const char *str, struct scene_obj_txt **txtp)
159 {
160         struct scene_obj_txt *txt;
161         int ret;
162
163         ret = expo_str(scn->expo, name, str_id, str);
164         if (ret < 0)
165                 return log_msg_ret("str", ret);
166         else if (ret != str_id)
167                 return log_msg_ret("id", -EEXIST);
168
169         ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
170                             sizeof(struct scene_obj_txt),
171                             (struct scene_obj **)&txt);
172         if (ret < 0)
173                 return log_msg_ret("obj", -ENOMEM);
174
175         txt->str_id = str_id;
176
177         if (txtp)
178                 *txtp = txt;
179
180         return txt->obj.id;
181 }
182
183 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
184                        uint font_size)
185 {
186         struct scene_obj_txt *txt;
187
188         txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
189         if (!txt)
190                 return log_msg_ret("find", -ENOENT);
191         txt->font_name = font_name;
192         txt->font_size = font_size;
193
194         return 0;
195 }
196
197 int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
198 {
199         struct scene_obj *obj;
200
201         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
202         if (!obj)
203                 return log_msg_ret("find", -ENOENT);
204         obj->dim.x = x;
205         obj->dim.y = y;
206
207         return 0;
208 }
209
210 int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
211 {
212         struct scene_obj *obj;
213
214         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
215         if (!obj)
216                 return log_msg_ret("find", -ENOENT);
217         obj->dim.w = w;
218         obj->dim.h = h;
219
220         return 0;
221 }
222
223 int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
224 {
225         int ret;
226
227         ret = scene_obj_flag_clrset(scn, id, SCENEOF_HIDE,
228                                     hide ? SCENEOF_HIDE : 0);
229         if (ret)
230                 return log_msg_ret("flg", ret);
231
232         return 0;
233 }
234
235 int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set)
236 {
237         struct scene_obj *obj;
238
239         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
240         if (!obj)
241                 return log_msg_ret("find", -ENOENT);
242         obj->flags &= ~clr;
243         obj->flags |= set;
244
245         return 0;
246 }
247
248 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
249 {
250         struct scene_obj *obj;
251
252         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
253         if (!obj)
254                 return log_msg_ret("find", -ENOENT);
255
256         switch (obj->type) {
257         case SCENEOBJT_NONE:
258         case SCENEOBJT_MENU:
259                 break;
260         case SCENEOBJT_IMAGE: {
261                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
262                 ulong width, height;
263                 uint bpix;
264
265                 video_bmp_get_info(img->data, &width, &height, &bpix);
266                 if (widthp)
267                         *widthp = width;
268                 return height;
269         }
270         case SCENEOBJT_TEXT: {
271                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
272                 struct expo *exp = scn->expo;
273                 struct vidconsole_bbox bbox;
274                 const char *str;
275                 int len, ret;
276
277                 str = expo_get_str(exp, txt->str_id);
278                 if (!str)
279                         return log_msg_ret("str", -ENOENT);
280                 len = strlen(str);
281
282                 /* if there is no console, make it up */
283                 if (!exp->cons) {
284                         if (widthp)
285                                 *widthp = 8 * len;
286                         return 16;
287                 }
288
289                 ret = vidconsole_measure(scn->expo->cons, txt->font_name,
290                                          txt->font_size, str, &bbox);
291                 if (ret)
292                         return log_msg_ret("mea", ret);
293                 if (widthp)
294                         *widthp = bbox.x1;
295
296                 return bbox.y1;
297         }
298         }
299
300         return 0;
301 }
302
303 /**
304  * scene_obj_render() - Render an object
305  *
306  */
307 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
308 {
309         struct scene *scn = obj->scene;
310         struct expo *exp = scn->expo;
311         struct udevice *dev = exp->display;
312         struct udevice *cons = text_mode ? NULL : exp->cons;
313         int x, y, ret;
314
315         x = obj->dim.x;
316         y = obj->dim.y;
317
318         switch (obj->type) {
319         case SCENEOBJT_NONE:
320                 break;
321         case SCENEOBJT_IMAGE: {
322                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
323
324                 if (!cons)
325                         return -ENOTSUPP;
326                 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
327                                         true);
328                 if (ret < 0)
329                         return log_msg_ret("img", ret);
330                 break;
331         }
332         case SCENEOBJT_TEXT: {
333                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
334                 const char *str;
335
336                 if (!cons)
337                         return -ENOTSUPP;
338
339                 if (txt->font_name || txt->font_size) {
340                         ret = vidconsole_select_font(cons,
341                                                      txt->font_name,
342                                                      txt->font_size);
343                 } else {
344                         ret = vidconsole_select_font(cons, NULL, 0);
345                 }
346                 if (ret && ret != -ENOSYS)
347                         return log_msg_ret("font", ret);
348                 str = expo_get_str(exp, txt->str_id);
349                 if (str) {
350                         struct video_priv *vid_priv;
351                         struct vidconsole_colour old;
352                         enum colour_idx fore, back;
353
354                         if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
355                                 fore = VID_BLACK;
356                                 back = VID_WHITE;
357                         } else {
358                                 fore = VID_LIGHT_GRAY;
359                                 back = VID_BLACK;
360                         }
361
362                         vid_priv = dev_get_uclass_priv(dev);
363                         if (obj->flags & SCENEOF_POINT) {
364                                 vidconsole_push_colour(cons, fore, back, &old);
365                                 video_fill_part(dev, x, y,
366                                                 x + obj->dim.w, y + obj->dim.h,
367                                                 vid_priv->colour_bg);
368                         }
369                         vidconsole_set_cursor_pos(cons, x, y);
370                         vidconsole_put_string(cons, str);
371                         if (obj->flags & SCENEOF_POINT)
372                                 vidconsole_pop_colour(cons, &old);
373                 }
374                 break;
375         }
376         case SCENEOBJT_MENU: {
377                 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
378
379                 if (exp->popup && (obj->flags & SCENEOF_OPEN)) {
380                         if (!cons)
381                                 return -ENOTSUPP;
382
383                         /* draw a background behind the menu items */
384                         scene_menu_render(menu);
385                 }
386                 /*
387                  * With a vidconsole, the text and item pointer are rendered as
388                  * normal objects so we don't need to do anything here. The menu
389                  * simply controls where they are positioned.
390                  */
391                 if (cons)
392                         return -ENOTSUPP;
393
394                 ret = scene_menu_display(menu);
395                 if (ret < 0)
396                         return log_msg_ret("img", ret);
397
398                 break;
399         }
400         }
401
402         return 0;
403 }
404
405 int scene_arrange(struct scene *scn)
406 {
407         struct scene_obj *obj;
408         int ret;
409
410         list_for_each_entry(obj, &scn->obj_head, sibling) {
411                 if (obj->type == SCENEOBJT_MENU) {
412                         struct scene_obj_menu *menu;
413
414                         menu = (struct scene_obj_menu *)obj,
415                         ret = scene_menu_arrange(scn, menu);
416                         if (ret)
417                                 return log_msg_ret("arr", ret);
418                 }
419         }
420
421         return 0;
422 }
423
424 int scene_render(struct scene *scn)
425 {
426         struct expo *exp = scn->expo;
427         struct scene_obj *obj;
428         int ret;
429
430         list_for_each_entry(obj, &scn->obj_head, sibling) {
431                 if (!(obj->flags & SCENEOF_HIDE)) {
432                         ret = scene_obj_render(obj, exp->text_mode);
433                         if (ret && ret != -ENOTSUPP)
434                                 return log_msg_ret("ren", ret);
435                 }
436         }
437
438         return 0;
439 }
440
441 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
442 {
443         struct scene_obj *obj;
444         int ret;
445
446         list_for_each_entry(obj, &scn->obj_head, sibling) {
447                 if (obj->type == SCENEOBJT_MENU) {
448                         struct scene_obj_menu *menu;
449
450                         menu = (struct scene_obj_menu *)obj,
451                         ret = scene_menu_send_key(scn, menu, key, event);
452                         if (ret)
453                                 return log_msg_ret("key", ret);
454                         break;
455                 }
456         }
457
458         return 0;
459 }
460
461 int scene_calc_dims(struct scene *scn, bool do_menus)
462 {
463         struct scene_obj *obj;
464         int ret;
465
466         list_for_each_entry(obj, &scn->obj_head, sibling) {
467                 switch (obj->type) {
468                 case SCENEOBJT_NONE:
469                 case SCENEOBJT_TEXT:
470                 case SCENEOBJT_IMAGE: {
471                         int width;
472
473                         if (!do_menus) {
474                                 ret = scene_obj_get_hw(scn, obj->id, &width);
475                                 if (ret < 0)
476                                         return log_msg_ret("get", ret);
477                                 obj->dim.w = width;
478                                 obj->dim.h = ret;
479                         }
480                         break;
481                 }
482                 case SCENEOBJT_MENU: {
483                         struct scene_obj_menu *menu;
484
485                         if (do_menus) {
486                                 menu = (struct scene_obj_menu *)obj;
487
488                                 ret = scene_menu_calc_dims(menu);
489                                 if (ret)
490                                         return log_msg_ret("men", ret);
491                         }
492                         break;
493                 }
494                 }
495         }
496
497         return 0;
498 }
499
500 int scene_apply_theme(struct scene *scn, struct expo_theme *theme)
501 {
502         struct scene_obj *obj;
503         int ret;
504
505         /* Avoid error-checking optional items */
506         scene_txt_set_font(scn, scn->title_id, NULL, theme->font_size);
507
508         list_for_each_entry(obj, &scn->obj_head, sibling) {
509                 switch (obj->type) {
510                 case SCENEOBJT_NONE:
511                 case SCENEOBJT_IMAGE:
512                 case SCENEOBJT_MENU:
513                         break;
514                 case SCENEOBJT_TEXT:
515                         scene_txt_set_font(scn, obj->id, NULL,
516                                            theme->font_size);
517                         break;
518                 }
519         }
520
521         ret = scene_arrange(scn);
522         if (ret)
523                 return log_msg_ret("arr", ret);
524
525         return 0;
526 }
527
528 void scene_set_highlight_id(struct scene *scn, uint id)
529 {
530         scn->highlight_id = id;
531 }
532
533 void scene_highlight_first(struct scene *scn)
534 {
535         struct scene_obj *obj;
536
537         list_for_each_entry(obj, &scn->obj_head, sibling) {
538                 switch (obj->type) {
539                 case SCENEOBJT_MENU:
540                         scene_set_highlight_id(scn, obj->id);
541                         return;
542                 default:
543                         break;
544                 }
545         }
546 }
547
548 int scene_set_open(struct scene *scn, uint id, bool open)
549 {
550         int ret;
551
552         ret = scene_obj_flag_clrset(scn, id, SCENEOF_OPEN,
553                                     open ? SCENEOF_OPEN : 0);
554         if (ret)
555                 return log_msg_ret("flg", ret);
556
557         return 0;
558 }
This page took 0.056725 seconds and 4 git commands to generate.