1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Implementation of a menu in a scene
4  *
5  * Copyright 2023 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8 
9 #define LOG_CATEGORY	LOGC_EXPO
10 
11 #include <expo.h>
12 #include <menu.h>
13 #include <log.h>
14 #include <video_console.h>
15 #include <linux/errno.h>
16 #include <linux/string.h>
17 #include "scene_internal.h"
18 
scene_textline(struct scene * scn,const char * name,uint id,uint max_chars,struct scene_obj_textline ** tlinep)19 int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars,
20 		   struct scene_obj_textline **tlinep)
21 {
22 	struct scene_obj_textline *tline;
23 	char *buf;
24 	int ret;
25 
26 	if (max_chars >= EXPO_MAX_CHARS)
27 		return log_msg_ret("chr", -E2BIG);
28 
29 	ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXTLINE,
30 			    sizeof(struct scene_obj_textline),
31 			    (struct scene_obj **)&tline);
32 	if (ret < 0)
33 		return log_msg_ret("obj", -ENOMEM);
34 	if (!abuf_init_size(&tline->buf, max_chars + 1))
35 		return log_msg_ret("buf", -ENOMEM);
36 	buf = abuf_data(&tline->buf);
37 	*buf = '\0';
38 	tline->pos = max_chars;
39 	tline->max_chars = max_chars;
40 
41 	if (tlinep)
42 		*tlinep = tline;
43 
44 	return tline->obj.id;
45 }
46 
scene_textline_calc_bbox(struct scene_obj_textline * tline,struct vidconsole_bbox * bbox,struct vidconsole_bbox * edit_bbox)47 void scene_textline_calc_bbox(struct scene_obj_textline *tline,
48 			      struct vidconsole_bbox *bbox,
49 			      struct vidconsole_bbox *edit_bbox)
50 {
51 	const struct expo_theme *theme = &tline->obj.scene->expo->theme;
52 
53 	bbox->valid = false;
54 	scene_bbox_union(tline->obj.scene, tline->label_id, 0, bbox);
55 	scene_bbox_union(tline->obj.scene, tline->edit_id, 0, bbox);
56 
57 	edit_bbox->valid = false;
58 	scene_bbox_union(tline->obj.scene, tline->edit_id, theme->menu_inset,
59 			 edit_bbox);
60 }
61 
scene_textline_calc_dims(struct scene_obj_textline * tline)62 int scene_textline_calc_dims(struct scene_obj_textline *tline)
63 {
64 	struct scene_obj *obj = &tline->obj;
65 	struct scene *scn = obj->scene;
66 	struct vidconsole_bbox bbox;
67 	struct scene_obj_txt *txt;
68 	int ret;
69 
70 	txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
71 	if (!txt)
72 		return log_msg_ret("dim", -ENOENT);
73 
74 	ret = vidconsole_nominal(scn->expo->cons, txt->gen.font_name,
75 				 txt->gen.font_size, tline->max_chars, &bbox);
76 	if (ret)
77 		return log_msg_ret("nom", ret);
78 
79 	if (bbox.valid) {
80 		obj->dims.x = bbox.x1 - bbox.x0;
81 		obj->dims.y = bbox.y1 - bbox.y0;
82 		if (!(obj->flags & SCENEOF_SIZE_VALID)) {
83 			obj->bbox.x1 = obj->bbox.x0 + obj->dims.x;
84 			obj->bbox.y1 = obj->bbox.y0 + obj->dims.y;
85 			obj->flags |= SCENEOF_SIZE_VALID;
86 		}
87 		scene_obj_set_size(scn, tline->edit_id,
88 				   obj->bbox.x1 - obj->bbox.x0,
89 				   obj->bbox.y1 - obj->bbox.y0);
90 	}
91 
92 	return 0;
93 }
94 
scene_textline_arrange(struct scene * scn,struct expo_arrange_info * arr,struct scene_obj_textline * tline)95 int scene_textline_arrange(struct scene *scn, struct expo_arrange_info *arr,
96 			   struct scene_obj_textline *tline)
97 {
98 	const bool open = tline->obj.flags & SCENEOF_OPEN;
99 	bool point;
100 	int x, y;
101 	int ret;
102 
103 	x = tline->obj.bbox.x0;
104 	y = tline->obj.bbox.y0;
105 	if (tline->label_id) {
106 		ret = scene_obj_set_pos(scn, tline->label_id,
107 					tline->obj.bbox.x0, y);
108 		if (ret < 0)
109 			return log_msg_ret("tit", ret);
110 
111 		ret = scene_obj_set_pos(scn, tline->edit_id,
112 					tline->obj.bbox.x0 + 200, y);
113 		if (ret < 0)
114 			return log_msg_ret("tit", ret);
115 
116 		ret = scene_obj_get_hw(scn, tline->label_id, NULL);
117 		if (ret < 0)
118 			return log_msg_ret("hei", ret);
119 
120 		y += ret * 2;
121 	}
122 
123 	point = scn->highlight_id == tline->obj.id;
124 	point &= !open;
125 	scene_obj_flag_clrset(scn, tline->edit_id, SCENEOF_POINT,
126 			      point ? SCENEOF_POINT : 0);
127 
128 	return 0;
129 }
130 
scene_textline_send_key(struct scene * scn,struct scene_obj_textline * tline,int key,struct expo_action * event)131 int scene_textline_send_key(struct scene *scn, struct scene_obj_textline *tline,
132 			    int key, struct expo_action *event)
133 {
134 	const bool open = tline->obj.flags & SCENEOF_OPEN;
135 
136 	log_debug("key=%d\n", key);
137 	switch (key) {
138 	case BKEY_QUIT:
139 		if (open) {
140 			event->type = EXPOACT_CLOSE;
141 			event->select.id = tline->obj.id;
142 
143 			/* Copy the backup text from the scene buffer */
144 			memcpy(abuf_data(&tline->buf), abuf_data(&scn->buf),
145 			       abuf_size(&scn->buf));
146 		} else {
147 			event->type = EXPOACT_QUIT;
148 			log_debug("menu quit\n");
149 		}
150 		break;
151 	case BKEY_SELECT:
152 		if (!open)
153 			break;
154 		event->type = EXPOACT_CLOSE;
155 		event->select.id = tline->obj.id;
156 		key = '\n';
157 		fallthrough;
158 	default: {
159 		struct udevice *cons = scn->expo->cons;
160 		int ret;
161 
162 		ret = vidconsole_entry_restore(cons, &scn->entry_save);
163 		if (ret)
164 			return log_msg_ret("sav", ret);
165 		ret = cread_line_process_ch(&scn->cls, key);
166 		ret = vidconsole_entry_save(cons, &scn->entry_save);
167 		if (ret)
168 			return log_msg_ret("sav", ret);
169 		break;
170 	}
171 	}
172 
173 	return 0;
174 }
175 
scene_textline_render_deps(struct scene * scn,struct scene_obj_textline * tline)176 int scene_textline_render_deps(struct scene *scn,
177 			       struct scene_obj_textline *tline)
178 {
179 	const bool open = tline->obj.flags & SCENEOF_OPEN;
180 	struct udevice *cons = scn->expo->cons;
181 	struct scene_obj_txt *txt;
182 	int ret;
183 
184 	scene_render_deps(scn, tline->label_id);
185 	scene_render_deps(scn, tline->edit_id);
186 
187 	/* show the vidconsole cursor if open */
188 	if (open) {
189 		/* get the position within the field */
190 		txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
191 		if (!txt)
192 			return log_msg_ret("cur", -ENOENT);
193 
194 		if (txt->gen.font_name || txt->gen.font_size) {
195 			ret = vidconsole_select_font(cons,
196 						     txt->gen.font_name,
197 						     txt->gen.font_size);
198 		} else {
199 			ret = vidconsole_select_font(cons, NULL, 0);
200 		}
201 
202 		ret = vidconsole_entry_restore(cons, &scn->entry_save);
203 		if (ret)
204 			return log_msg_ret("sav", ret);
205 
206 		vidconsole_set_cursor_visible(cons, true, txt->obj.bbox.x0,
207 					      txt->obj.bbox.y0, scn->cls.num);
208 	}
209 
210 	return 0;
211 }
212 
scene_textline_open(struct scene * scn,struct scene_obj_textline * tline)213 int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline)
214 {
215 	struct udevice *cons = scn->expo->cons;
216 	struct scene_obj_txt *txt;
217 	int ret;
218 
219 	/* Copy the text into the scene buffer in case the edit is cancelled */
220 	memcpy(abuf_data(&scn->buf), abuf_data(&tline->buf),
221 	       abuf_size(&scn->buf));
222 
223 	/* get the position of the editable */
224 	txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
225 	if (!txt)
226 		return log_msg_ret("cur", -ENOENT);
227 
228 	vidconsole_set_cursor_pos(cons, txt->obj.bbox.x0, txt->obj.bbox.y0);
229 	vidconsole_entry_start(cons);
230 	cli_cread_init(&scn->cls, abuf_data(&tline->buf), tline->max_chars);
231 	scn->cls.insert = true;
232 	ret = vidconsole_entry_save(cons, &scn->entry_save);
233 	if (ret)
234 		return log_msg_ret("sav", ret);
235 
236 	return 0;
237 }
238