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