1// 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 9#define LOG_CATEGORY LOGC_EXPO 10 11#include <common.h> 12#include <dm.h> 13#include <expo.h> 14#include <malloc.h> 15#include <video.h> 16#include "scene_internal.h" 17 18int expo_new(const char *name, void *priv, struct expo **expp) 19{ 20 struct expo *exp; 21 22 exp = calloc(1, sizeof(struct expo)); 23 if (!exp) 24 return log_msg_ret("expo", -ENOMEM); 25 exp->name = strdup(name); 26 if (!exp->name) { 27 free(exp); 28 return log_msg_ret("name", -ENOMEM); 29 } 30 exp->priv = priv; 31 INIT_LIST_HEAD(&exp->scene_head); 32 INIT_LIST_HEAD(&exp->str_head); 33 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 59uint resolve_id(struct expo *exp, uint id) 60{ 61 log_debug("resolve id %d\n", id); 62 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 75int 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{ 104 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 112 exp->display = dev; 113 exp->cons = cons; 114 115 return 0; 116} 117 118int 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 142void expo_set_text_mode(struct expo *exp, bool text_mode) 143{ 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{ 161 struct scene *scn; 162 int ret; 163 164 scn = expo_lookup_scene_id(exp, scene_id); 165 if (!scn) 166 return log_msg_ret("id", -ENOENT); 167 ret = scene_arrange(scn); 168 if (ret) 169 return log_msg_ret("arr", ret); 170 171 exp->scene_id = scene_id; 172 173 return 0; 174} 175 176int 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 188int 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; 193 enum colour_idx back; 194 u32 colour; 195 int ret; 196 197 back = CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK) ? VID_BLACK : VID_WHITE; 198 colour = video_index_to_colour(vid_priv, back); 199 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); 232 233 /* arrange it to get any changes */ 234 ret = scene_arrange(scn); 235 if (ret) 236 return log_msg_ret("arr", ret); 237 } 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} 249 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); 260 ofnode_read_u32(node, "menu-inset", &theme->menu_inset); 261 ofnode_read_u32(node, "menuitem-gap-y", &theme->menuitem_gap_y); 262 263 list_for_each_entry(scn, &exp->scene_head, sibling) { 264 ret = scene_apply_theme(scn, theme); 265 if (ret) 266 return log_msg_ret("app", ret); 267 } 268 269 return 0; 270} 271 272int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter, 273 void *priv) 274{ 275 struct scene *scn; 276 int ret; 277 278 list_for_each_entry(scn, &exp->scene_head, sibling) { 279 ret = scene_iter_objs(scn, iter, priv); 280 if (ret) 281 return log_msg_ret("wr", ret); 282 } 283 284 return 0; 285} 286