blob: 301bbfa5f9afabdd7cf28095cce0466f9ce48494 [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>
Simon Glass137b4422025-05-02 08:46:16 -060013#include <log.h>
Simon Glassd8adbe92023-01-06 08:52:36 -060014#include <malloc.h>
Simon Glass137b4422025-05-02 08:46:16 -060015#include <menu.h>
Simon Glassd8adbe92023-01-06 08:52:36 -060016#include <video.h>
Simon Glass137b4422025-05-02 08:46:16 -060017#include <watchdog.h>
18#include <linux/delay.h>
Simon Glassd8adbe92023-01-06 08:52:36 -060019#include "scene_internal.h"
20
21int expo_new(const char *name, void *priv, struct expo **expp)
22{
23 struct expo *exp;
24
25 exp = calloc(1, sizeof(struct expo));
26 if (!exp)
27 return log_msg_ret("expo", -ENOMEM);
28 exp->name = strdup(name);
29 if (!exp->name) {
30 free(exp);
31 return log_msg_ret("name", -ENOMEM);
32 }
33 exp->priv = priv;
34 INIT_LIST_HEAD(&exp->scene_head);
35 INIT_LIST_HEAD(&exp->str_head);
Simon Glass53a0a2f2024-10-14 16:31:57 -060036 exp->next_id = EXPOID_BASE_ID;
Simon Glass683d8832025-05-02 08:46:15 -060037 cli_ch_init(&exp->cch);
Simon Glassd8adbe92023-01-06 08:52:36 -060038
39 *expp = exp;
40
41 return 0;
42}
43
44static void estr_destroy(struct expo_string *estr)
45{
46 free(estr);
47}
48
49void expo_destroy(struct expo *exp)
50{
51 struct scene *scn, *next;
52 struct expo_string *estr, *enext;
53
54 list_for_each_entry_safe(scn, next, &exp->scene_head, sibling)
55 scene_destroy(scn);
56
57 list_for_each_entry_safe(estr, enext, &exp->str_head, sibling)
58 estr_destroy(estr);
59
60 free(exp->name);
61 free(exp);
62}
63
Simon Glass6e9e4152023-06-01 10:22:47 -060064uint resolve_id(struct expo *exp, uint id)
65{
Simon Glass61300722023-06-01 10:23:01 -060066 log_debug("resolve id %d\n", id);
Simon Glass6e9e4152023-06-01 10:22:47 -060067 if (!id)
68 id = exp->next_id++;
69 else if (id >= exp->next_id)
70 exp->next_id = id + 1;
71
72 return id;
73}
74
75void expo_set_dynamic_start(struct expo *exp, uint dyn_start)
76{
77 exp->next_id = dyn_start;
78}
79
Simon Glassd8adbe92023-01-06 08:52:36 -060080int expo_str(struct expo *exp, const char *name, uint id, const char *str)
81{
82 struct expo_string *estr;
83
84 estr = calloc(1, sizeof(struct expo_string));
85 if (!estr)
86 return log_msg_ret("obj", -ENOMEM);
87
88 estr->id = resolve_id(exp, id);
89 estr->str = str;
90 list_add_tail(&estr->sibling, &exp->str_head);
91
92 return estr->id;
93}
94
95const char *expo_get_str(struct expo *exp, uint id)
96{
97 struct expo_string *estr;
98
99 list_for_each_entry(estr, &exp->str_head, sibling) {
100 if (estr->id == id)
101 return estr->str;
102 }
103
104 return NULL;
105}
106
107int expo_set_display(struct expo *exp, struct udevice *dev)
108{
Simon Glass67e2af12023-06-01 10:22:34 -0600109 struct udevice *cons;
110 int ret;
111
112 ret = device_find_first_child_by_uclass(dev, UCLASS_VIDEO_CONSOLE,
113 &cons);
114 if (ret)
115 return log_msg_ret("con", ret);
116
Simon Glassd8adbe92023-01-06 08:52:36 -0600117 exp->display = dev;
Simon Glass67e2af12023-06-01 10:22:34 -0600118 exp->cons = cons;
Simon Glassd8adbe92023-01-06 08:52:36 -0600119
120 return 0;
121}
122
Simon Glass7a960052023-06-01 10:22:52 -0600123int expo_calc_dims(struct expo *exp)
124{
125 struct scene *scn;
126 int ret;
127
128 if (!exp->cons)
129 return log_msg_ret("dim", -ENOTSUPP);
130
131 list_for_each_entry(scn, &exp->scene_head, sibling) {
132 /*
133 * Do the menus last so that all the menus' text objects
134 * are dimensioned
135 */
136 ret = scene_calc_dims(scn, false);
137 if (ret)
138 return log_msg_ret("scn", ret);
139 ret = scene_calc_dims(scn, true);
140 if (ret)
141 return log_msg_ret("scn", ret);
142 }
143
144 return 0;
145}
146
Simon Glassb2c40342023-06-01 10:22:37 -0600147void expo_set_text_mode(struct expo *exp, bool text_mode)
Simon Glassd8adbe92023-01-06 08:52:36 -0600148{
149 exp->text_mode = text_mode;
150}
151
152struct scene *expo_lookup_scene_id(struct expo *exp, uint scene_id)
153{
154 struct scene *scn;
155
156 list_for_each_entry(scn, &exp->scene_head, sibling) {
157 if (scn->id == scene_id)
158 return scn;
159 }
160
161 return NULL;
162}
163
164int expo_set_scene_id(struct expo *exp, uint scene_id)
165{
Simon Glassd7e32a82023-06-01 10:22:35 -0600166 struct scene *scn;
167 int ret;
168
169 scn = expo_lookup_scene_id(exp, scene_id);
170 if (!scn)
Simon Glassd8adbe92023-01-06 08:52:36 -0600171 return log_msg_ret("id", -ENOENT);
Simon Glassd7e32a82023-06-01 10:22:35 -0600172 ret = scene_arrange(scn);
173 if (ret)
174 return log_msg_ret("arr", ret);
175
Simon Glassd8adbe92023-01-06 08:52:36 -0600176 exp->scene_id = scene_id;
177
178 return 0;
179}
180
Simon Glass12f57732023-06-01 10:22:58 -0600181int expo_first_scene_id(struct expo *exp)
182{
183 struct scene *scn;
184
185 if (list_empty(&exp->scene_head))
186 return -ENOENT;
187
188 scn = list_first_entry(&exp->scene_head, struct scene, sibling);
189
190 return scn->id;
191}
192
Simon Glassd8adbe92023-01-06 08:52:36 -0600193int expo_render(struct expo *exp)
194{
195 struct udevice *dev = exp->display;
196 struct video_priv *vid_priv = dev_get_uclass_priv(dev);
197 struct scene *scn = NULL;
Simon Glass9ac53fb2023-10-01 19:14:41 -0600198 enum colour_idx back;
Simon Glassd8adbe92023-01-06 08:52:36 -0600199 u32 colour;
200 int ret;
201
Simon Glass21320da2025-04-02 06:29:33 +1300202 back = vid_priv->white_on_black ? VID_BLACK : VID_WHITE;
Simon Glass9ac53fb2023-10-01 19:14:41 -0600203 colour = video_index_to_colour(vid_priv, back);
Simon Glassd8adbe92023-01-06 08:52:36 -0600204 ret = video_fill(dev, colour);
205 if (ret)
206 return log_msg_ret("fill", ret);
207
208 if (exp->scene_id) {
209 scn = expo_lookup_scene_id(exp, exp->scene_id);
210 if (!scn)
211 return log_msg_ret("scn", -ENOENT);
212
213 ret = scene_render(scn);
214 if (ret)
215 return log_msg_ret("ren", ret);
216 }
217
218 video_sync(dev, true);
219
220 return scn ? 0 : -ECHILD;
221}
222
223int expo_send_key(struct expo *exp, int key)
224{
225 struct scene *scn = NULL;
226
227 if (exp->scene_id) {
228 int ret;
229
230 scn = expo_lookup_scene_id(exp, exp->scene_id);
231 if (!scn)
232 return log_msg_ret("scn", -ENOENT);
233
234 ret = scene_send_key(scn, key, &exp->action);
235 if (ret)
236 return log_msg_ret("key", ret);
Simon Glassd7e32a82023-06-01 10:22:35 -0600237
238 /* arrange it to get any changes */
239 ret = scene_arrange(scn);
240 if (ret)
241 return log_msg_ret("arr", ret);
Simon Glassd8adbe92023-01-06 08:52:36 -0600242 }
243
244 return scn ? 0 : -ECHILD;
245}
246
247int expo_action_get(struct expo *exp, struct expo_action *act)
248{
249 *act = exp->action;
250 exp->action.type = EXPOACT_NONE;
251
252 return act->type == EXPOACT_NONE ? -EAGAIN : 0;
253}
Simon Glassc999e172023-06-01 10:22:53 -0600254
255int expo_apply_theme(struct expo *exp, ofnode node)
256{
257 struct scene *scn;
258 struct expo_theme *theme = &exp->theme;
259 int ret;
260
261 log_debug("Applying theme %s\n", ofnode_get_name(node));
262
263 memset(theme, '\0', sizeof(struct expo_theme));
264 ofnode_read_u32(node, "font-size", &theme->font_size);
Simon Glass86f1ac52023-06-01 10:23:00 -0600265 ofnode_read_u32(node, "menu-inset", &theme->menu_inset);
266 ofnode_read_u32(node, "menuitem-gap-y", &theme->menuitem_gap_y);
Simon Glass377f18e2024-10-14 16:31:55 -0600267 ofnode_read_u32(node, "menu-title-margin-x",
268 &theme->menu_title_margin_x);
Simon Glassc999e172023-06-01 10:22:53 -0600269
270 list_for_each_entry(scn, &exp->scene_head, sibling) {
271 ret = scene_apply_theme(scn, theme);
272 if (ret)
273 return log_msg_ret("app", ret);
274 }
275
276 return 0;
277}
Simon Glasse90acd82023-08-14 16:40:23 -0600278
279int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter,
280 void *priv)
281{
282 struct scene *scn;
283 int ret;
284
285 list_for_each_entry(scn, &exp->scene_head, sibling) {
286 ret = scene_iter_objs(scn, iter, priv);
287 if (ret)
288 return log_msg_ret("wr", ret);
289 }
290
291 return 0;
292}
Simon Glass137b4422025-05-02 08:46:16 -0600293
294int expo_poll(struct expo *exp, struct expo_action *act)
295{
296 int ichar, key, ret;
297
298 ret = expo_render(exp);
299 if (ret)
300 return log_msg_ret("ere", ret);
301
302 ichar = cli_ch_process(&exp->cch, 0);
303 if (!ichar) {
304 while (!ichar && !tstc()) {
305 schedule();
306 mdelay(2);
307 ichar = cli_ch_process(&exp->cch, -ETIMEDOUT);
308 }
309 if (!ichar) {
310 ichar = getchar();
311 ichar = cli_ch_process(&exp->cch, ichar);
312 }
313 }
314
315 key = 0;
316 if (ichar) {
317 key = bootmenu_conv_key(ichar);
318 if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA)
319 key = ichar;
320 }
321 if (!key)
322 return -EAGAIN;
323
324 ret = expo_send_key(exp, key);
325 if (ret)
326 return log_msg_ret("epk", ret);
327 ret = expo_action_get(exp, act);
328 if (ret)
329 return log_msg_ret("eag", ret);
330
331 return 0;
332}