expo: Add width and height to objects
[pandora-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 <sjg@chromium.org>
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_hide(struct scene *scn, uint id, bool hide)
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->hide = hide;
218
219         return 0;
220 }
221
222 int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
223 {
224         struct scene_obj *obj;
225
226         obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
227         if (!obj)
228                 return log_msg_ret("find", -ENOENT);
229
230         switch (obj->type) {
231         case SCENEOBJT_NONE:
232         case SCENEOBJT_MENU:
233                 break;
234         case SCENEOBJT_IMAGE: {
235                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
236                 ulong width, height;
237                 uint bpix;
238
239                 video_bmp_get_info(img->data, &width, &height, &bpix);
240                 if (widthp)
241                         *widthp = width;
242                 return height;
243         }
244         case SCENEOBJT_TEXT: {
245                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
246                 struct expo *exp = scn->expo;
247
248                 if (widthp)
249                         *widthp = 16; /* fake value for now */
250                 if (txt->font_size)
251                         return txt->font_size;
252                 if (exp->display)
253                         return video_default_font_height(exp->display);
254
255                 /* use a sensible default */
256                 return 16;
257         }
258         }
259
260         return 0;
261 }
262
263 /**
264  * scene_obj_render() - Render an object
265  *
266  */
267 static int scene_obj_render(struct scene_obj *obj, bool text_mode)
268 {
269         struct scene *scn = obj->scene;
270         struct expo *exp = scn->expo;
271         struct udevice *dev = exp->display;
272         struct udevice *cons = text_mode ? NULL : exp->cons;
273         int x, y, ret;
274
275         x = obj->dim.x;
276         y = obj->dim.y;
277
278         switch (obj->type) {
279         case SCENEOBJT_NONE:
280                 break;
281         case SCENEOBJT_IMAGE: {
282                 struct scene_obj_img *img = (struct scene_obj_img *)obj;
283
284                 if (!cons)
285                         return -ENOTSUPP;
286                 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
287                                         true);
288                 if (ret < 0)
289                         return log_msg_ret("img", ret);
290                 break;
291         }
292         case SCENEOBJT_TEXT: {
293                 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
294                 const char *str;
295
296                 if (!cons)
297                         return -ENOTSUPP;
298
299                 if (txt->font_name || txt->font_size) {
300                         ret = vidconsole_select_font(cons,
301                                                      txt->font_name,
302                                                      txt->font_size);
303                 } else {
304                         ret = vidconsole_select_font(cons, NULL, 0);
305                 }
306                 if (ret && ret != -ENOSYS)
307                         return log_msg_ret("font", ret);
308                 vidconsole_set_cursor_pos(cons, x, y);
309                 str = expo_get_str(exp, txt->str_id);
310                 if (str)
311                         vidconsole_put_string(cons, str);
312                 break;
313         }
314         case SCENEOBJT_MENU: {
315                 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
316                 /*
317                  * With a vidconsole, the text and item pointer are rendered as
318                  * normal objects so we don't need to do anything here. The menu
319                  * simply controls where they are positioned.
320                  */
321                 if (cons)
322                         return -ENOTSUPP;
323
324                 ret = scene_menu_display(menu);
325                 if (ret < 0)
326                         return log_msg_ret("img", ret);
327
328                 break;
329         }
330         }
331
332         return 0;
333 }
334
335 int scene_arrange(struct scene *scn)
336 {
337         struct scene_obj *obj;
338         int ret;
339
340         list_for_each_entry(obj, &scn->obj_head, sibling) {
341                 if (obj->type == SCENEOBJT_MENU) {
342                         struct scene_obj_menu *menu;
343
344                         menu = (struct scene_obj_menu *)obj,
345                         ret = scene_menu_arrange(scn, menu);
346                         if (ret)
347                                 return log_msg_ret("arr", ret);
348                 }
349         }
350
351         return 0;
352 }
353
354 int scene_render(struct scene *scn)
355 {
356         struct expo *exp = scn->expo;
357         struct scene_obj *obj;
358         int ret;
359
360         list_for_each_entry(obj, &scn->obj_head, sibling) {
361                 if (!obj->hide) {
362                         ret = scene_obj_render(obj, exp->text_mode);
363                         if (ret && ret != -ENOTSUPP)
364                                 return log_msg_ret("ren", ret);
365                 }
366         }
367
368         return 0;
369 }
370
371 int scene_send_key(struct scene *scn, int key, struct expo_action *event)
372 {
373         struct scene_obj *obj;
374         int ret;
375
376         list_for_each_entry(obj, &scn->obj_head, sibling) {
377                 if (obj->type == SCENEOBJT_MENU) {
378                         struct scene_obj_menu *menu;
379
380                         menu = (struct scene_obj_menu *)obj,
381                         ret = scene_menu_send_key(scn, menu, key, event);
382                         if (ret)
383                                 return log_msg_ret("key", ret);
384                         break;
385                 }
386         }
387
388         return 0;
389 }