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