blob: 786f665f53c0d183b1bb53961b82f54c1de94602 [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 Glassd8adbe92023-01-06 08:52:36 -060033
34 *expp = exp;
35
36 return 0;
37}
38
39static void estr_destroy(struct expo_string *estr)
40{
41 free(estr);
42}
43
44void expo_destroy(struct expo *exp)
45{
46 struct scene *scn, *next;
47 struct expo_string *estr, *enext;
48
49 list_for_each_entry_safe(scn, next, &exp->scene_head, sibling)
50 scene_destroy(scn);
51
52 list_for_each_entry_safe(estr, enext, &exp->str_head, sibling)
53 estr_destroy(estr);
54
55 free(exp->name);
56 free(exp);
57}
58
Simon Glass6e9e4152023-06-01 10:22:47 -060059uint resolve_id(struct expo *exp, uint id)
60{
Simon Glass61300722023-06-01 10:23:01 -060061 log_debug("resolve id %d\n", id);
Simon Glass6e9e4152023-06-01 10:22:47 -060062 if (!id)
63 id = exp->next_id++;
64 else if (id >= exp->next_id)
65 exp->next_id = id + 1;
66
67 return id;
68}
69
70void expo_set_dynamic_start(struct expo *exp, uint dyn_start)
71{
72 exp->next_id = dyn_start;
73}
74
Simon Glassd8adbe92023-01-06 08:52:36 -060075int expo_str(struct expo *exp, const char *name, uint id, const char *str)
76{
77 struct expo_string *estr;
78
79 estr = calloc(1, sizeof(struct expo_string));
80 if (!estr)
81 return log_msg_ret("obj", -ENOMEM);
82
83 estr->id = resolve_id(exp, id);
84 estr->str = str;
85 list_add_tail(&estr->sibling, &exp->str_head);
86
87 return estr->id;
88}
89
90const char *expo_get_str(struct expo *exp, uint id)
91{
92 struct expo_string *estr;
93
94 list_for_each_entry(estr, &exp->str_head, sibling) {
95 if (estr->id == id)
96 return estr->str;
97 }
98
99 return NULL;
100}
101
102int expo_set_display(struct expo *exp, struct udevice *dev)
103{
Simon Glass67e2af12023-06-01 10:22:34 -0600104 struct udevice *cons;
105 int ret;
106
107 ret = device_find_first_child_by_uclass(dev, UCLASS_VIDEO_CONSOLE,
108 &cons);
109 if (ret)
110 return log_msg_ret("con", ret);
111
Simon Glassd8adbe92023-01-06 08:52:36 -0600112 exp->display = dev;
Simon Glass67e2af12023-06-01 10:22:34 -0600113 exp->cons = cons;
Simon Glassd8adbe92023-01-06 08:52:36 -0600114
115 return 0;
116}
117
Simon Glass7a960052023-06-01 10:22:52 -0600118int expo_calc_dims(struct expo *exp)
119{
120 struct scene *scn;
121 int ret;
122
123 if (!exp->cons)
124 return log_msg_ret("dim", -ENOTSUPP);
125
126 list_for_each_entry(scn, &exp->scene_head, sibling) {
127 /*
128 * Do the menus last so that all the menus' text objects
129 * are dimensioned
130 */
131 ret = scene_calc_dims(scn, false);
132 if (ret)
133 return log_msg_ret("scn", ret);
134 ret = scene_calc_dims(scn, true);
135 if (ret)
136 return log_msg_ret("scn", ret);
137 }
138
139 return 0;
140}
141
Simon Glassb2c40342023-06-01 10:22:37 -0600142void expo_set_text_mode(struct expo *exp, bool text_mode)
Simon Glassd8adbe92023-01-06 08:52:36 -0600143{
144 exp->text_mode = text_mode;
145}
146
147struct scene *expo_lookup_scene_id(struct expo *exp, uint scene_id)
148{
149 struct scene *scn;
150
151 list_for_each_entry(scn, &exp->scene_head, sibling) {
152 if (scn->id == scene_id)
153 return scn;
154 }
155
156 return NULL;
157}
158
159int expo_set_scene_id(struct expo *exp, uint scene_id)
160{
Simon Glassd7e32a82023-06-01 10:22:35 -0600161 struct scene *scn;
162 int ret;
163
164 scn = expo_lookup_scene_id(exp, scene_id);
165 if (!scn)
Simon Glassd8adbe92023-01-06 08:52:36 -0600166 return log_msg_ret("id", -ENOENT);
Simon Glassd7e32a82023-06-01 10:22:35 -0600167 ret = scene_arrange(scn);
168 if (ret)
169 return log_msg_ret("arr", ret);
170
Simon Glassd8adbe92023-01-06 08:52:36 -0600171 exp->scene_id = scene_id;
172
173 return 0;
174}
175
Simon Glass12f57732023-06-01 10:22:58 -0600176int expo_first_scene_id(struct expo *exp)
177{
178 struct scene *scn;
179
180 if (list_empty(&exp->scene_head))
181 return -ENOENT;
182
183 scn = list_first_entry(&exp->scene_head, struct scene, sibling);
184
185 return scn->id;
186}
187
Simon Glassd8adbe92023-01-06 08:52:36 -0600188int expo_render(struct expo *exp)
189{
190 struct udevice *dev = exp->display;
191 struct video_priv *vid_priv = dev_get_uclass_priv(dev);
192 struct scene *scn = NULL;
Simon Glass9ac53fb2023-10-01 19:14:41 -0600193 enum colour_idx back;
Simon Glassd8adbe92023-01-06 08:52:36 -0600194 u32 colour;
195 int ret;
196
Simon Glass9ac53fb2023-10-01 19:14:41 -0600197 back = CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK) ? VID_BLACK : VID_WHITE;
198 colour = video_index_to_colour(vid_priv, back);
Simon Glassd8adbe92023-01-06 08:52:36 -0600199 ret = video_fill(dev, colour);
200 if (ret)
201 return log_msg_ret("fill", ret);
202
203 if (exp->scene_id) {
204 scn = expo_lookup_scene_id(exp, exp->scene_id);
205 if (!scn)
206 return log_msg_ret("scn", -ENOENT);
207
208 ret = scene_render(scn);
209 if (ret)
210 return log_msg_ret("ren", ret);
211 }
212
213 video_sync(dev, true);
214
215 return scn ? 0 : -ECHILD;
216}
217
218int expo_send_key(struct expo *exp, int key)
219{
220 struct scene *scn = NULL;
221
222 if (exp->scene_id) {
223 int ret;
224
225 scn = expo_lookup_scene_id(exp, exp->scene_id);
226 if (!scn)
227 return log_msg_ret("scn", -ENOENT);
228
229 ret = scene_send_key(scn, key, &exp->action);
230 if (ret)
231 return log_msg_ret("key", ret);
Simon Glassd7e32a82023-06-01 10:22:35 -0600232
233 /* arrange it to get any changes */
234 ret = scene_arrange(scn);
235 if (ret)
236 return log_msg_ret("arr", ret);
Simon Glassd8adbe92023-01-06 08:52:36 -0600237 }
238
239 return scn ? 0 : -ECHILD;
240}
241
242int expo_action_get(struct expo *exp, struct expo_action *act)
243{
244 *act = exp->action;
245 exp->action.type = EXPOACT_NONE;
246
247 return act->type == EXPOACT_NONE ? -EAGAIN : 0;
248}
Simon Glassc999e172023-06-01 10:22:53 -0600249
250int expo_apply_theme(struct expo *exp, ofnode node)
251{
252 struct scene *scn;
253 struct expo_theme *theme = &exp->theme;
254 int ret;
255
256 log_debug("Applying theme %s\n", ofnode_get_name(node));
257
258 memset(theme, '\0', sizeof(struct expo_theme));
259 ofnode_read_u32(node, "font-size", &theme->font_size);
Simon Glass86f1ac52023-06-01 10:23:00 -0600260 ofnode_read_u32(node, "menu-inset", &theme->menu_inset);
261 ofnode_read_u32(node, "menuitem-gap-y", &theme->menuitem_gap_y);
Simon Glass377f18e2024-10-14 16:31:55 -0600262 ofnode_read_u32(node, "menu-title-margin-x",
263 &theme->menu_title_margin_x);
Simon Glassc999e172023-06-01 10:22:53 -0600264
265 list_for_each_entry(scn, &exp->scene_head, sibling) {
266 ret = scene_apply_theme(scn, theme);
267 if (ret)
268 return log_msg_ret("app", ret);
269 }
270
271 return 0;
272}
Simon Glasse90acd82023-08-14 16:40:23 -0600273
274int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter,
275 void *priv)
276{
277 struct scene *scn;
278 int ret;
279
280 list_for_each_entry(scn, &exp->scene_head, sibling) {
281 ret = scene_iter_objs(scn, iter, priv);
282 if (ret)
283 return log_msg_ret("wr", ret);
284 }
285
286 return 0;
287}