blob: 43c978e6ee803738ce9585f804888de8451efd08 [file] [log] [blame]
Simon Glass5e2607a2023-01-06 08:52:37 -06001// 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
Simon Glassc98cb512023-06-01 10:22:43 -06009#define LOG_CATEGORY LOGC_EXPO
10
Simon Glass5e2607a2023-01-06 08:52:37 -060011#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
21uint resolve_id(struct expo *exp, uint id)
22{
23 if (!id)
24 id = exp->next_id++;
25 else if (id >= exp->next_id)
26 exp->next_id = id + 1;
27
28 return id;
29}
30
31int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
32{
33 struct scene *scn;
34
35 scn = calloc(1, sizeof(struct scene));
36 if (!scn)
37 return log_msg_ret("expo", -ENOMEM);
38 scn->name = strdup(name);
39 if (!scn->name) {
40 free(scn);
41 return log_msg_ret("name", -ENOMEM);
42 }
43
44 INIT_LIST_HEAD(&scn->obj_head);
45 scn->id = resolve_id(exp, id);
46 scn->expo = exp;
47 list_add_tail(&scn->sibling, &exp->scene_head);
48
49 *scnp = scn;
50
51 return scn->id;
52}
53
54void scene_obj_destroy(struct scene_obj *obj)
55{
56 if (obj->type == SCENEOBJT_MENU)
57 scene_menu_destroy((struct scene_obj_menu *)obj);
58 free(obj->name);
59 free(obj);
60}
61
62void scene_destroy(struct scene *scn)
63{
64 struct scene_obj *obj, *next;
65
66 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
67 scene_obj_destroy(obj);
68
69 free(scn->name);
Simon Glass5e2607a2023-01-06 08:52:37 -060070 free(scn);
71}
72
Simon Glassdef898c2023-06-01 10:22:27 -060073int scene_title_set(struct scene *scn, uint id)
Simon Glass5e2607a2023-01-06 08:52:37 -060074{
Simon Glassdef898c2023-06-01 10:22:27 -060075 scn->title_id = id;
Simon Glass5e2607a2023-01-06 08:52:37 -060076
77 return 0;
78}
79
80int scene_obj_count(struct scene *scn)
81{
82 struct scene_obj *obj;
83 int count = 0;
84
85 list_for_each_entry(obj, &scn->obj_head, sibling)
86 count++;
87
88 return count;
89}
90
91void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type)
92{
93 struct scene_obj *obj;
94
95 list_for_each_entry(obj, &scn->obj_head, sibling) {
96 if (obj->id == id &&
97 (type == SCENEOBJT_NONE || obj->type == type))
98 return obj;
99 }
100
101 return NULL;
102}
103
104int scene_obj_add(struct scene *scn, const char *name, uint id,
105 enum scene_obj_t type, uint size, struct scene_obj **objp)
106{
107 struct scene_obj *obj;
108
109 obj = calloc(1, size);
110 if (!obj)
111 return log_msg_ret("obj", -ENOMEM);
112 obj->name = strdup(name);
113 if (!obj->name) {
114 free(obj);
115 return log_msg_ret("name", -ENOMEM);
116 }
117
118 obj->id = resolve_id(scn->expo, id);
119 obj->scene = scn;
120 obj->type = type;
121 list_add_tail(&obj->sibling, &scn->obj_head);
122 *objp = obj;
123
124 return obj->id;
125}
126
127int scene_img(struct scene *scn, const char *name, uint id, char *data,
128 struct scene_obj_img **imgp)
129{
130 struct scene_obj_img *img;
131 int ret;
132
133 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
134 sizeof(struct scene_obj_img),
135 (struct scene_obj **)&img);
136 if (ret < 0)
137 return log_msg_ret("obj", -ENOMEM);
138
139 img->data = data;
140
141 if (imgp)
142 *imgp = img;
143
144 return img->obj.id;
145}
146
147int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
148 struct scene_obj_txt **txtp)
149{
150 struct scene_obj_txt *txt;
151 int ret;
152
153 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
154 sizeof(struct scene_obj_txt),
155 (struct scene_obj **)&txt);
156 if (ret < 0)
157 return log_msg_ret("obj", -ENOMEM);
158
159 txt->str_id = str_id;
160
161 if (txtp)
162 *txtp = txt;
163
164 return txt->obj.id;
165}
166
167int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
168 const char *str, struct scene_obj_txt **txtp)
169{
170 struct scene_obj_txt *txt;
171 int ret;
172
173 ret = expo_str(scn->expo, name, str_id, str);
174 if (ret < 0)
175 return log_msg_ret("str", ret);
176 else if (ret != str_id)
177 return log_msg_ret("id", -EEXIST);
178
179 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
180 sizeof(struct scene_obj_txt),
181 (struct scene_obj **)&txt);
182 if (ret < 0)
183 return log_msg_ret("obj", -ENOMEM);
184
185 txt->str_id = str_id;
186
187 if (txtp)
188 *txtp = txt;
189
190 return txt->obj.id;
191}
192
193int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
194 uint font_size)
195{
196 struct scene_obj_txt *txt;
197
198 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
199 if (!txt)
200 return log_msg_ret("find", -ENOENT);
201 txt->font_name = font_name;
202 txt->font_size = font_size;
203
204 return 0;
205}
206
207int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
208{
209 struct scene_obj *obj;
210
211 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
212 if (!obj)
213 return log_msg_ret("find", -ENOENT);
214 obj->x = x;
215 obj->y = y;
Simon Glass5e2607a2023-01-06 08:52:37 -0600216
217 return 0;
218}
219
220int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
221{
222 struct scene_obj *obj;
223
224 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
225 if (!obj)
226 return log_msg_ret("find", -ENOENT);
227 obj->hide = hide;
228
229 return 0;
230}
231
232int scene_obj_get_hw(struct scene *scn, uint id, int *widthp)
233{
234 struct scene_obj *obj;
235
236 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
237 if (!obj)
238 return log_msg_ret("find", -ENOENT);
239
240 switch (obj->type) {
241 case SCENEOBJT_NONE:
242 case SCENEOBJT_MENU:
243 break;
244 case SCENEOBJT_IMAGE: {
245 struct scene_obj_img *img = (struct scene_obj_img *)obj;
246 ulong width, height;
247 uint bpix;
248
249 video_bmp_get_info(img->data, &width, &height, &bpix);
250 if (widthp)
251 *widthp = width;
252 return height;
253 }
254 case SCENEOBJT_TEXT: {
255 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
256 struct expo *exp = scn->expo;
257
258 if (widthp)
259 *widthp = 16; /* fake value for now */
260 if (txt->font_size)
261 return txt->font_size;
262 if (exp->display)
263 return video_default_font_height(exp->display);
264
265 /* use a sensible default */
266 return 16;
267 }
268 }
269
270 return 0;
271}
272
273/**
274 * scene_obj_render() - Render an object
275 *
276 */
277static int scene_obj_render(struct scene_obj *obj, bool text_mode)
278{
279 struct scene *scn = obj->scene;
280 struct expo *exp = scn->expo;
Simon Glass42b18492023-06-01 10:22:34 -0600281 struct udevice *dev = exp->display;
282 struct udevice *cons = text_mode ? NULL : exp->cons;
Simon Glass5e2607a2023-01-06 08:52:37 -0600283 int x, y, ret;
284
Simon Glass5e2607a2023-01-06 08:52:37 -0600285 x = obj->x;
286 y = obj->y;
287
288 switch (obj->type) {
289 case SCENEOBJT_NONE:
290 break;
291 case SCENEOBJT_IMAGE: {
292 struct scene_obj_img *img = (struct scene_obj_img *)obj;
293
294 if (!cons)
295 return -ENOTSUPP;
296 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
297 true);
298 if (ret < 0)
299 return log_msg_ret("img", ret);
300 break;
301 }
302 case SCENEOBJT_TEXT: {
303 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
304 const char *str;
305
306 if (!cons)
307 return -ENOTSUPP;
308
309 if (txt->font_name || txt->font_size) {
310 ret = vidconsole_select_font(cons,
311 txt->font_name,
312 txt->font_size);
313 } else {
314 ret = vidconsole_select_font(cons, NULL, 0);
315 }
316 if (ret && ret != -ENOSYS)
317 return log_msg_ret("font", ret);
318 vidconsole_set_cursor_pos(cons, x, y);
319 str = expo_get_str(exp, txt->str_id);
320 if (str)
321 vidconsole_put_string(cons, str);
322 break;
323 }
324 case SCENEOBJT_MENU: {
325 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
326 /*
327 * With a vidconsole, the text and item pointer are rendered as
328 * normal objects so we don't need to do anything here. The menu
329 * simply controls where they are positioned.
330 */
331 if (cons)
332 return -ENOTSUPP;
333
334 ret = scene_menu_display(menu);
335 if (ret < 0)
336 return log_msg_ret("img", ret);
337
338 break;
339 }
340 }
341
342 return 0;
343}
344
345int scene_arrange(struct scene *scn)
346{
347 struct scene_obj *obj;
348 int ret;
349
350 list_for_each_entry(obj, &scn->obj_head, sibling) {
351 if (obj->type == SCENEOBJT_MENU) {
352 struct scene_obj_menu *menu;
353
354 menu = (struct scene_obj_menu *)obj,
355 ret = scene_menu_arrange(scn, menu);
356 if (ret)
357 return log_msg_ret("arr", ret);
358 }
359 }
360
361 return 0;
362}
363
364int scene_render(struct scene *scn)
365{
366 struct expo *exp = scn->expo;
367 struct scene_obj *obj;
368 int ret;
369
370 list_for_each_entry(obj, &scn->obj_head, sibling) {
371 if (!obj->hide) {
372 ret = scene_obj_render(obj, exp->text_mode);
373 if (ret && ret != -ENOTSUPP)
374 return log_msg_ret("ren", ret);
375 }
376 }
377
378 return 0;
379}
380
381int scene_send_key(struct scene *scn, int key, struct expo_action *event)
382{
383 struct scene_obj *obj;
384 int ret;
385
386 list_for_each_entry(obj, &scn->obj_head, sibling) {
387 if (obj->type == SCENEOBJT_MENU) {
388 struct scene_obj_menu *menu;
389
390 menu = (struct scene_obj_menu *)obj,
391 ret = scene_menu_send_key(scn, menu, key, event);
392 if (ret)
393 return log_msg_ret("key", ret);
Simon Glass5e2607a2023-01-06 08:52:37 -0600394 break;
395 }
396 }
397
398 return 0;
399}