blob: 9c042f16fe7799a8237513e83104455ecbef6b6c [file] [log] [blame]
Simon Glassd8adbe92023-01-06 08:52:36 -06001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Implementation of a expo, a collection of scenes providing menu options
4 *
5 * Copyright 2022 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
Simon Glassfe4c1e22023-06-01 10:22:43 -06009#define LOG_CATEGORY LOGC_EXPO
10
Simon Glassd8adbe92023-01-06 08:52:36 -060011#include <dm.h>
12#include <expo.h>
13#include <malloc.h>
14#include <video.h>
15#include "scene_internal.h"
16
17int expo_new(const char *name, void *priv, struct expo **expp)
18{
19 struct expo *exp;
20
21 exp = calloc(1, sizeof(struct expo));
22 if (!exp)
23 return log_msg_ret("expo", -ENOMEM);
24 exp->name = strdup(name);
25 if (!exp->name) {
26 free(exp);
27 return log_msg_ret("name", -ENOMEM);
28 }
29 exp->priv = priv;
30 INIT_LIST_HEAD(&exp->scene_head);
31 INIT_LIST_HEAD(&exp->str_head);
Simon Glass53a0a2f2024-10-14 16:31:57 -060032 exp->next_id = EXPOID_BASE_ID;
Simon Glass683d8832025-05-02 08:46:15 -060033 cli_ch_init(&exp->cch);
Simon Glassd8adbe92023-01-06 08:52:36 -060034
35 *expp = exp;
36
37 return 0;
38}
39
40static void estr_destroy(struct expo_string *estr)
41{
42 free(estr);
43}
44
45void expo_destroy(struct expo *exp)
46{
47 struct scene *scn, *next;
48 struct expo_string *estr, *enext;
49
50 list_for_each_entry_safe(scn, next, &exp->scene_head, sibling)
51 scene_destroy(scn);
52
53 list_for_each_entry_safe(estr, enext, &exp->str_head, sibling)
54 estr_destroy(estr);
55
56 free(exp->name);
57 free(exp);
58}
59
Simon Glass6e9e4152023-06-01 10:22:47 -060060uint resolve_id(struct expo *exp, uint id)
61{
Simon Glass61300722023-06-01 10:23:01 -060062 log_debug("resolve id %d\n", id);
Simon Glass6e9e4152023-06-01 10:22:47 -060063 if (!id)
64 id = exp->next_id++;
65 else if (id >= exp->next_id)
66 exp->next_id = id + 1;
67
68 return id;
69}
70
71void expo_set_dynamic_start(struct expo *exp, uint dyn_start)
72{
73 exp->next_id = dyn_start;
74}
75
Simon Glassd8adbe92023-01-06 08:52:36 -060076int expo_str(struct expo *exp, const char *name, uint id, const char *str)
77{
78 struct expo_string *estr;
79
80 estr = calloc(1, sizeof(struct expo_string));
81 if (!estr)
82 return log_msg_ret("obj", -ENOMEM);
83
84 estr->id = resolve_id(exp, id);
85 estr->str = str;
86 list_add_tail(&estr->sibling, &exp->str_head);
87
88 return estr->id;
89}
90
91const char *expo_get_str(struct expo *exp, uint id)
92{
93 struct expo_string *estr;
94
95 list_for_each_entry(estr, &exp->str_head, sibling) {
96 if (estr->id == id)
97 return estr->str;
98 }
99
100 return NULL;
101}
102
103int expo_set_display(struct expo *exp, struct udevice *dev)
104{
Simon Glass67e2af12023-06-01 10:22:34 -0600105 struct udevice *cons;
106 int ret;
107
108 ret = device_find_first_child_by_uclass(dev, UCLASS_VIDEO_CONSOLE,
109 &cons);
110 if (ret)
111 return log_msg_ret("con", ret);
112
Simon Glassd8adbe92023-01-06 08:52:36 -0600113 exp->display = dev;
Simon Glass67e2af12023-06-01 10:22:34 -0600114 exp->cons = cons;
Simon Glassd8adbe92023-01-06 08:52:36 -0600115
116 return 0;
117}
118
Simon Glass7a960052023-06-01 10:22:52 -0600119int expo_calc_dims(struct expo *exp)
120{
121 struct scene *scn;
122 int ret;
123
124 if (!exp->cons)
125 return log_msg_ret("dim", -ENOTSUPP);
126
127 list_for_each_entry(scn, &exp->scene_head, sibling) {
128 /*
129 * Do the menus last so that all the menus' text objects
130 * are dimensioned
131 */
132 ret = scene_calc_dims(scn, false);
133 if (ret)
134 return log_msg_ret("scn", ret);
135 ret = scene_calc_dims(scn, true);
136 if (ret)
137 return log_msg_ret("scn", ret);
138 }
139
140 return 0;
141}
142
Simon Glassb2c40342023-06-01 10:22:37 -0600143void expo_set_text_mode(struct expo *exp, bool text_mode)
Simon Glassd8adbe92023-01-06 08:52:36 -0600144{
145 exp->text_mode = text_mode;
146}
147
148struct scene *expo_lookup_scene_id(struct expo *exp, uint scene_id)
149{
150 struct scene *scn;
151
152 list_for_each_entry(scn, &exp->scene_head, sibling) {
153 if (scn->id == scene_id)
154 return scn;
155 }
156
157 return NULL;
158}
159
160int expo_set_scene_id(struct expo *exp, uint scene_id)
161{
Simon Glassd7e32a82023-06-01 10:22:35 -0600162 struct scene *scn;
163 int ret;
164
165 scn = expo_lookup_scene_id(exp, scene_id);
166 if (!scn)
Simon Glassd8adbe92023-01-06 08:52:36 -0600167 return log_msg_ret("id", -ENOENT);
Simon Glassd7e32a82023-06-01 10:22:35 -0600168 ret = scene_arrange(scn);
169 if (ret)
170 return log_msg_ret("arr", ret);
171
Simon Glassd8adbe92023-01-06 08:52:36 -0600172 exp->scene_id = scene_id;
173
174 return 0;
175}
176
Simon Glass12f57732023-06-01 10:22:58 -0600177int expo_first_scene_id(struct expo *exp)
178{
179 struct scene *scn;
180
181 if (list_empty(&exp->scene_head))
182 return -ENOENT;
183
184 scn = list_first_entry(&exp->scene_head, struct scene, sibling);
185
186 return scn->id;
187}
188
Simon Glassd8adbe92023-01-06 08:52:36 -0600189int expo_render(struct expo *exp)
190{
191 struct udevice *dev = exp->display;
192 struct video_priv *vid_priv = dev_get_uclass_priv(dev);
193 struct scene *scn = NULL;
Simon Glass9ac53fb2023-10-01 19:14:41 -0600194 enum colour_idx back;
Simon Glassd8adbe92023-01-06 08:52:36 -0600195 u32 colour;
196 int ret;
197
Simon Glass21320da2025-04-02 06:29:33 +1300198 back = vid_priv->white_on_black ? VID_BLACK : VID_WHITE;
Simon Glass9ac53fb2023-10-01 19:14:41 -0600199 colour = video_index_to_colour(vid_priv, back);
Simon Glassd8adbe92023-01-06 08:52:36 -0600200 ret = video_fill(dev, colour);
201 if (ret)
202 return log_msg_ret("fill", ret);
203
204 if (exp->scene_id) {
205 scn = expo_lookup_scene_id(exp, exp->scene_id);
206 if (!scn)
207 return log_msg_ret("scn", -ENOENT);
208
209 ret = scene_render(scn);
210 if (ret)
211 return log_msg_ret("ren", ret);
212 }
213
214 video_sync(dev, true);
215
216 return scn ? 0 : -ECHILD;
217}
218
219int expo_send_key(struct expo *exp, int key)
220{
221 struct scene *scn = NULL;
222
223 if (exp->scene_id) {
224 int ret;
225
226 scn = expo_lookup_scene_id(exp, exp->scene_id);
227 if (!scn)
228 return log_msg_ret("scn", -ENOENT);
229
230 ret = scene_send_key(scn, key, &exp->action);
231 if (ret)
232 return log_msg_ret("key", ret);
Simon Glassd7e32a82023-06-01 10:22:35 -0600233
234 /* arrange it to get any changes */
235 ret = scene_arrange(scn);
236 if (ret)
237 return log_msg_ret("arr", ret);
Simon Glassd8adbe92023-01-06 08:52:36 -0600238 }
239
240 return scn ? 0 : -ECHILD;
241}
242
243int expo_action_get(struct expo *exp, struct expo_action *act)
244{
245 *act = exp->action;
246 exp->action.type = EXPOACT_NONE;
247
248 return act->type == EXPOACT_NONE ? -EAGAIN : 0;
249}
Simon Glassc999e172023-06-01 10:22:53 -0600250
251int expo_apply_theme(struct expo *exp, ofnode node)
252{
253 struct scene *scn;
254 struct expo_theme *theme = &exp->theme;
255 int ret;
256
257 log_debug("Applying theme %s\n", ofnode_get_name(node));
258
259 memset(theme, '\0', sizeof(struct expo_theme));
260 ofnode_read_u32(node, "font-size", &theme->font_size);
Simon Glass86f1ac52023-06-01 10:23:00 -0600261 ofnode_read_u32(node, "menu-inset", &theme->menu_inset);
262 ofnode_read_u32(node, "menuitem-gap-y", &theme->menuitem_gap_y);
Simon Glass377f18e2024-10-14 16:31:55 -0600263 ofnode_read_u32(node, "menu-title-margin-x",
264 &theme->menu_title_margin_x);
Simon Glassc999e172023-06-01 10:22:53 -0600265
266 list_for_each_entry(scn, &exp->scene_head, sibling) {
267 ret = scene_apply_theme(scn, theme);
268 if (ret)
269 return log_msg_ret("app", ret);
270 }
271
272 return 0;
273}
Simon Glasse90acd82023-08-14 16:40:23 -0600274
275int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter,
276 void *priv)
277{
278 struct scene *scn;
279 int ret;
280
281 list_for_each_entry(scn, &exp->scene_head, sibling) {
282 ret = scene_iter_objs(scn, iter, priv);
283 if (ret)
284 return log_msg_ret("wr", ret);
285 }
286
287 return 0;
288}