blob: d2f77c008cf73f834efdac45165b791f27bc66c0 [file] [log] [blame]
Simon Glass0a4d14b2023-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
9#include <common.h>
10#include <dm.h>
11#include <expo.h>
12#include <malloc.h>
13#include <mapmem.h>
14#include <video.h>
15#include <video_console.h>
16#include <linux/input.h>
17#include "scene_internal.h"
18
19uint resolve_id(struct expo *exp, uint id)
20{
21 if (!id)
22 id = exp->next_id++;
23 else if (id >= exp->next_id)
24 exp->next_id = id + 1;
25
26 return id;
27}
28
29int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
30{
31 struct scene *scn;
32
33 scn = calloc(1, sizeof(struct scene));
34 if (!scn)
35 return log_msg_ret("expo", -ENOMEM);
36 scn->name = strdup(name);
37 if (!scn->name) {
38 free(scn);
39 return log_msg_ret("name", -ENOMEM);
40 }
41
42 INIT_LIST_HEAD(&scn->obj_head);
43 scn->id = resolve_id(exp, id);
44 scn->expo = exp;
45 list_add_tail(&scn->sibling, &exp->scene_head);
46
47 *scnp = scn;
48
49 return scn->id;
50}
51
52void scene_obj_destroy(struct scene_obj *obj)
53{
54 if (obj->type == SCENEOBJT_MENU)
55 scene_menu_destroy((struct scene_obj_menu *)obj);
56 free(obj->name);
57 free(obj);
58}
59
60void scene_destroy(struct scene *scn)
61{
62 struct scene_obj *obj, *next;
63
64 list_for_each_entry_safe(obj, next, &scn->obj_head, sibling)
65 scene_obj_destroy(obj);
66
67 free(scn->name);
Simon Glass0a4d14b2023-01-06 08:52:37 -060068 free(scn);
69}
70
Simon Glassea274b62023-06-01 10:22:27 -060071int scene_title_set(struct scene *scn, uint id)
Simon Glass0a4d14b2023-01-06 08:52:37 -060072{
Simon Glassea274b62023-06-01 10:22:27 -060073 scn->title_id = id;
Simon Glass0a4d14b2023-01-06 08:52:37 -060074
75 return 0;
76}
77
78int scene_obj_count(struct scene *scn)
79{
80 struct scene_obj *obj;
81 int count = 0;
82
83 list_for_each_entry(obj, &scn->obj_head, sibling)
84 count++;
85
86 return count;
87}
88
89void *scene_obj_find(struct scene *scn, uint id, enum scene_obj_t type)
90{
91 struct scene_obj *obj;
92
93 list_for_each_entry(obj, &scn->obj_head, sibling) {
94 if (obj->id == id &&
95 (type == SCENEOBJT_NONE || obj->type == type))
96 return obj;
97 }
98
99 return NULL;
100}
101
102int scene_obj_add(struct scene *scn, const char *name, uint id,
103 enum scene_obj_t type, uint size, struct scene_obj **objp)
104{
105 struct scene_obj *obj;
106
107 obj = calloc(1, size);
108 if (!obj)
109 return log_msg_ret("obj", -ENOMEM);
110 obj->name = strdup(name);
111 if (!obj->name) {
112 free(obj);
113 return log_msg_ret("name", -ENOMEM);
114 }
115
116 obj->id = resolve_id(scn->expo, id);
117 obj->scene = scn;
118 obj->type = type;
119 list_add_tail(&obj->sibling, &scn->obj_head);
120 *objp = obj;
121
122 return obj->id;
123}
124
125int scene_img(struct scene *scn, const char *name, uint id, char *data,
126 struct scene_obj_img **imgp)
127{
128 struct scene_obj_img *img;
129 int ret;
130
131 ret = scene_obj_add(scn, name, id, SCENEOBJT_IMAGE,
132 sizeof(struct scene_obj_img),
133 (struct scene_obj **)&img);
134 if (ret < 0)
135 return log_msg_ret("obj", -ENOMEM);
136
137 img->data = data;
138
139 if (imgp)
140 *imgp = img;
141
142 return img->obj.id;
143}
144
145int scene_txt(struct scene *scn, const char *name, uint id, uint str_id,
146 struct scene_obj_txt **txtp)
147{
148 struct scene_obj_txt *txt;
149 int ret;
150
151 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
152 sizeof(struct scene_obj_txt),
153 (struct scene_obj **)&txt);
154 if (ret < 0)
155 return log_msg_ret("obj", -ENOMEM);
156
157 txt->str_id = str_id;
158
159 if (txtp)
160 *txtp = txt;
161
162 return txt->obj.id;
163}
164
165int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
166 const char *str, struct scene_obj_txt **txtp)
167{
168 struct scene_obj_txt *txt;
169 int ret;
170
171 ret = expo_str(scn->expo, name, str_id, str);
172 if (ret < 0)
173 return log_msg_ret("str", ret);
174 else if (ret != str_id)
175 return log_msg_ret("id", -EEXIST);
176
177 ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT,
178 sizeof(struct scene_obj_txt),
179 (struct scene_obj **)&txt);
180 if (ret < 0)
181 return log_msg_ret("obj", -ENOMEM);
182
183 txt->str_id = str_id;
184
185 if (txtp)
186 *txtp = txt;
187
188 return txt->obj.id;
189}
190
191int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
192 uint font_size)
193{
194 struct scene_obj_txt *txt;
195
196 txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
197 if (!txt)
198 return log_msg_ret("find", -ENOENT);
199 txt->font_name = font_name;
200 txt->font_size = font_size;
201
202 return 0;
203}
204
205int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
206{
207 struct scene_obj *obj;
208
209 obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
210 if (!obj)
211 return log_msg_ret("find", -ENOENT);
212 obj->x = x;
213 obj->y = y;
214 if (obj->type == SCENEOBJT_MENU)
215 scene_menu_arrange(scn, (struct scene_obj_menu *)obj);
216
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;
281 struct udevice *cons, *dev = exp->display;
282 int x, y, ret;
283
284 cons = NULL;
285 if (!text_mode) {
286 ret = device_find_first_child_by_uclass(dev,
287 UCLASS_VIDEO_CONSOLE,
288 &cons);
289 }
290
291 x = obj->x;
292 y = obj->y;
293
294 switch (obj->type) {
295 case SCENEOBJT_NONE:
296 break;
297 case SCENEOBJT_IMAGE: {
298 struct scene_obj_img *img = (struct scene_obj_img *)obj;
299
300 if (!cons)
301 return -ENOTSUPP;
302 ret = video_bmp_display(dev, map_to_sysmem(img->data), x, y,
303 true);
304 if (ret < 0)
305 return log_msg_ret("img", ret);
306 break;
307 }
308 case SCENEOBJT_TEXT: {
309 struct scene_obj_txt *txt = (struct scene_obj_txt *)obj;
310 const char *str;
311
312 if (!cons)
313 return -ENOTSUPP;
314
315 if (txt->font_name || txt->font_size) {
316 ret = vidconsole_select_font(cons,
317 txt->font_name,
318 txt->font_size);
319 } else {
320 ret = vidconsole_select_font(cons, NULL, 0);
321 }
322 if (ret && ret != -ENOSYS)
323 return log_msg_ret("font", ret);
324 vidconsole_set_cursor_pos(cons, x, y);
325 str = expo_get_str(exp, txt->str_id);
326 if (str)
327 vidconsole_put_string(cons, str);
328 break;
329 }
330 case SCENEOBJT_MENU: {
331 struct scene_obj_menu *menu = (struct scene_obj_menu *)obj;
332 /*
333 * With a vidconsole, the text and item pointer are rendered as
334 * normal objects so we don't need to do anything here. The menu
335 * simply controls where they are positioned.
336 */
337 if (cons)
338 return -ENOTSUPP;
339
340 ret = scene_menu_display(menu);
341 if (ret < 0)
342 return log_msg_ret("img", ret);
343
344 break;
345 }
346 }
347
348 return 0;
349}
350
351int scene_arrange(struct scene *scn)
352{
353 struct scene_obj *obj;
354 int ret;
355
356 list_for_each_entry(obj, &scn->obj_head, sibling) {
357 if (obj->type == SCENEOBJT_MENU) {
358 struct scene_obj_menu *menu;
359
360 menu = (struct scene_obj_menu *)obj,
361 ret = scene_menu_arrange(scn, menu);
362 if (ret)
363 return log_msg_ret("arr", ret);
364 }
365 }
366
367 return 0;
368}
369
370int scene_render(struct scene *scn)
371{
372 struct expo *exp = scn->expo;
373 struct scene_obj *obj;
374 int ret;
375
376 list_for_each_entry(obj, &scn->obj_head, sibling) {
377 if (!obj->hide) {
378 ret = scene_obj_render(obj, exp->text_mode);
379 if (ret && ret != -ENOTSUPP)
380 return log_msg_ret("ren", ret);
381 }
382 }
383
384 return 0;
385}
386
387int scene_send_key(struct scene *scn, int key, struct expo_action *event)
388{
389 struct scene_obj *obj;
390 int ret;
391
392 list_for_each_entry(obj, &scn->obj_head, sibling) {
393 if (obj->type == SCENEOBJT_MENU) {
394 struct scene_obj_menu *menu;
395
396 menu = (struct scene_obj_menu *)obj,
397 ret = scene_menu_send_key(scn, menu, key, event);
398 if (ret)
399 return log_msg_ret("key", ret);
400
401 /* only allow one menu */
402 ret = scene_menu_arrange(scn, menu);
403 if (ret)
404 return log_msg_ret("arr", ret);
405 break;
406 }
407 }
408
409 return 0;
410}