master
c 358 lines 8.98 KB
Raw
1 /*
2 * SPDX-License-Identifier: MIT
3 * QEMU VC
4 */
5 #include "qemu/osdep.h"
6
7 #include "chardev/char.h"
8 #include "qapi/error.h"
9 #include "qemu/option.h"
10 #include "qemu/queue.h"
11 #include "qom/compat-properties.h"
12 #include "ui/console.h"
13 #include "ui/vgafont.h"
14 #include "ui/vt100.h"
15
16 #include "pixman.h"
17 #include "trace.h"
18 #include "console-priv.h"
19
20 typedef struct QemuTextConsole {
21 QemuConsole parent;
22
23 QemuVT100 vt;
24 Chardev *chr;
25 } QemuTextConsole;
26
27 typedef QemuConsoleClass QemuTextConsoleClass;
28
29 OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE)
30
31 typedef struct QemuFixedTextConsole {
32 QemuTextConsole parent;
33 } QemuFixedTextConsole;
34
35 typedef QemuTextConsoleClass QemuFixedTextConsoleClass;
36
37 OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE)
38
39 struct VCChardev {
40 Chardev parent;
41
42 ChardevVCEncoding encoding;
43 QemuTextConsole *console;
44 };
45 typedef struct VCChardev VCChardev;
46
47 static char *
48 qemu_text_console_get_label(const QemuConsole *c)
49 {
50 QemuTextConsole *tc = QEMU_TEXT_CONSOLE(c);
51
52 return tc->chr ? g_strdup(tc->chr->label) : NULL;
53 }
54
55 static void qemu_text_console_out_flush(QemuTextConsole *s)
56 {
57 uint32_t len, avail;
58
59 len = qemu_chr_be_can_write(s->chr);
60 avail = fifo8_num_used(&s->vt.out_fifo);
61 while (len > 0 && avail > 0) {
62 const uint8_t *buf;
63 uint32_t size;
64
65 buf = fifo8_pop_bufptr(&s->vt.out_fifo, MIN(len, avail), &size);
66 qemu_chr_be_write(s->chr, buf, size);
67 len = qemu_chr_be_can_write(s->chr);
68 avail -= size;
69 }
70 }
71
72 /* called when an ascii key is pressed */
73 void qemu_text_console_handle_keysym(QemuTextConsole *s, int keysym)
74 {
75 vt100_keysym(&s->vt, keysym);
76 }
77
78 static void text_console_update(void *opaque, uint32_t *chardata)
79 {
80 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
81 int i, j, src;
82
83 if (s->vt.text_x[0] <= s->vt.text_x[1]) {
84 src = (s->vt.y_base + s->vt.text_y[0]) * s->vt.width;
85 chardata += s->vt.text_y[0] * s->vt.width;
86 for (i = s->vt.text_y[0]; i <= s->vt.text_y[1]; i ++)
87 for (j = 0; j < s->vt.width; j++, src++) {
88 *chardata++ = ATTR2CHTYPE(s->vt.cells[src].ch,
89 s->vt.cells[src].t_attrib.fgcol,
90 s->vt.cells[src].t_attrib.bgcol,
91 s->vt.cells[src].t_attrib.bold);
92 }
93 qemu_console_text_update(QEMU_CONSOLE(s), s->vt.text_x[0], s->vt.text_y[0],
94 s->vt.text_x[1] - s->vt.text_x[0], i - s->vt.text_y[0]);
95 s->vt.text_x[0] = s->vt.width;
96 s->vt.text_y[0] = s->vt.height;
97 s->vt.text_x[1] = 0;
98 s->vt.text_y[1] = 0;
99 }
100 if (s->vt.cursor_invalidate) {
101 qemu_console_text_set_cursor(QEMU_CONSOLE(s), s->vt.x, s->vt.y);
102 s->vt.cursor_invalidate = 0;
103 }
104 }
105
106 #define TYPE_CHARDEV_VC "chardev-vc"
107 DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
108 TYPE_CHARDEV_VC)
109
110 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
111 {
112 VCChardev *drv = VC_CHARDEV(chr);
113 QemuTextConsole *s = drv->console;
114
115 return vt100_input(&s->vt, buf, len);
116 }
117
118 static void text_console_invalidate(void *opaque)
119 {
120 QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
121
122 if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
123 vt100_set_image(&s->vt, QEMU_CONSOLE(s)->surface->image);
124 }
125 vt100_refresh(&s->vt);
126 }
127
128 static void
129 qemu_text_console_finalize(Object *obj)
130 {
131 QemuTextConsole *s = QEMU_TEXT_CONSOLE(obj);
132
133 vt100_fini(&s->vt);
134 }
135
136 static void
137 qemu_text_console_class_init(ObjectClass *oc, const void *data)
138 {
139 QemuConsoleClass *cc = QEMU_CONSOLE_CLASS(oc);
140
141 cc->get_label = qemu_text_console_get_label;
142 }
143
144 static const GraphicHwOps text_console_ops = {
145 .invalidate = text_console_invalidate,
146 .text_update = text_console_update,
147 };
148
149 static void
150 qemu_text_console_init(Object *obj)
151 {
152 QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
153
154 QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
155 QEMU_CONSOLE(c)->hw = c;
156 }
157
158 static void
159 qemu_fixed_text_console_finalize(Object *obj)
160 {
161 }
162
163 static void
164 qemu_fixed_text_console_class_init(ObjectClass *oc, const void *data)
165 {
166 }
167
168 static void
169 qemu_fixed_text_console_init(Object *obj)
170 {
171 }
172
173 static void vc_chr_accept_input(Chardev *chr)
174 {
175 VCChardev *drv = VC_CHARDEV(chr);
176
177 qemu_text_console_out_flush(drv->console);
178 }
179
180 static void vc_chr_set_echo(Chardev *chr, bool echo)
181 {
182 VCChardev *drv = VC_CHARDEV(chr);
183
184 drv->console->vt.echo = echo;
185 }
186
187 void qemu_text_console_update_size(QemuTextConsole *c)
188 {
189 qemu_console_text_resize(QEMU_CONSOLE(c), c->vt.width, c->vt.height);
190 }
191
192 static void text_console_image_update(QemuVT100 *vt, int x, int y, int width, int height)
193 {
194 QemuTextConsole *console = container_of(vt, QemuTextConsole, vt);
195
196 qemu_console_update(QEMU_CONSOLE(console), x, y, width, height);
197 }
198
199 static void text_console_out_flush(QemuVT100 *vt)
200 {
201 QemuTextConsole *console = container_of(vt, QemuTextConsole, vt);
202
203 qemu_text_console_out_flush(console);
204 }
205
206 static bool vc_chr_open(Chardev *chr, ChardevBackend *backend, Error **errp)
207 {
208 ChardevVC *vc = backend->u.vc.data;
209 VCChardev *drv = VC_CHARDEV(chr);
210 QemuTextConsole *s;
211 unsigned width = 0;
212 unsigned height = 0;
213
214 if (vc->has_width) {
215 width = vc->width;
216 } else if (vc->has_cols) {
217 width = vc->cols * FONT_WIDTH;
218 }
219
220 if (vc->has_height) {
221 height = vc->height;
222 } else if (vc->has_rows) {
223 height = vc->rows * FONT_HEIGHT;
224 }
225
226 trace_console_txt_new(width, height);
227 if (width == 0 || height == 0) {
228 s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE));
229 width = 80 * FONT_WIDTH;
230 height = 24 * FONT_HEIGHT;
231 } else {
232 s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
233 }
234
235 qemu_console_set_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
236 if (vc->has_encoding) {
237 drv->encoding = vc->encoding;
238 }
239 vt100_init(&s->vt, QEMU_CONSOLE(s)->surface->image,
240 drv->encoding,
241 text_console_image_update,
242 text_console_out_flush);
243
244 s->chr = chr;
245 drv->console = s;
246
247 if (chr->label) {
248 char *msg;
249
250 s->vt.t_attrib.bgcol = QEMU_COLOR_BLUE;
251 msg = g_strdup_printf("%s console\r\n", chr->label);
252 qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
253 g_free(msg);
254 s->vt.t_attrib = TEXT_ATTRIBUTES_DEFAULT;
255 }
256
257 qemu_chr_be_event(chr, CHR_EVENT_OPENED);
258 qemu_console_notify(QEMU_CONSOLE_ADDED, QEMU_CONSOLE(s));
259 return true;
260 }
261
262 static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp)
263 {
264 int val;
265 const char *str;
266 ChardevVC *vc;
267
268 backend->type = CHARDEV_BACKEND_KIND_VC;
269 vc = backend->u.vc.data = g_new0(ChardevVC, 1);
270 qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
271
272 val = qemu_opt_get_number(opts, "width", 0);
273 if (val != 0) {
274 vc->has_width = true;
275 vc->width = val;
276 }
277
278 val = qemu_opt_get_number(opts, "height", 0);
279 if (val != 0) {
280 vc->has_height = true;
281 vc->height = val;
282 }
283
284 val = qemu_opt_get_number(opts, "cols", 0);
285 if (val != 0) {
286 vc->has_cols = true;
287 vc->cols = val;
288 }
289
290 val = qemu_opt_get_number(opts, "rows", 0);
291 if (val != 0) {
292 vc->has_rows = true;
293 vc->rows = val;
294 }
295
296 str = qemu_opt_get(opts, "encoding");
297 if (str) {
298 int cs = qapi_enum_parse(&ChardevVCEncoding_lookup, str, -1, errp);
299 if (cs < 0) {
300 return;
301 }
302 vc->has_encoding = true;
303 vc->encoding = cs;
304 }
305 }
306
307 CHARDEV_VC_ENCODING_PROPERTY_DEFINE(VC_CHARDEV)
308
309 static void char_vc_class_init(ObjectClass *oc, const void *data)
310 {
311 ChardevClass *cc = CHARDEV_CLASS(oc);
312
313 cc->chr_parse = vc_chr_parse;
314 cc->chr_open = vc_chr_open;
315 cc->chr_write = vc_chr_write;
316 cc->chr_accept_input = vc_chr_accept_input;
317 cc->chr_set_echo = vc_chr_set_echo;
318 cc->supports_size_opts = true;
319 cc->supports_encoding_opts = true;
320
321 chardev_vc_add_encoding_prop(oc, get_encoding, set_encoding);
322 }
323
324 static void char_vc_init(Object *obj)
325 {
326 VCChardev *vc = VC_CHARDEV(obj);
327
328 vc->encoding = CHARDEV_VC_ENCODING_UTF8;
329 }
330
331 static void char_vc_finalize(Object *obj)
332 {
333 VCChardev *vc = VC_CHARDEV(obj);
334 QemuConsole *con = QEMU_CONSOLE(vc->console);
335
336 if (con) {
337 qemu_console_notify(QEMU_CONSOLE_REMOVED, con);
338 object_unref(con);
339 }
340 }
341
342 static const TypeInfo char_vc_type_info = {
343 .name = TYPE_CHARDEV_VC,
344 .parent = TYPE_CHARDEV,
345 .instance_size = sizeof(VCChardev),
346 .instance_init = char_vc_init,
347 .instance_post_init = object_apply_compat_props,
348 .instance_finalize = char_vc_finalize,
349 .class_init = char_vc_class_init,
350 };
351
352 void qemu_console_early_init(void)
353 {
354 /* set the default vc driver */
355 if (!object_class_by_name(TYPE_CHARDEV_VC)) {
356 type_register_static(&char_vc_type_info);
357 }
358 }