]> git.proxmox.com Git - mirror_qemu.git/blame - ui/console.c
tpm: reorganize headers and split hardware part
[mirror_qemu.git] / ui / console.c
CommitLineData
e7f0ad58
FB
1/*
2 * QEMU graphical console
5fafdf24 3 *
e7f0ad58 4 * Copyright (c) 2004 Fabrice Bellard
5fafdf24 5 *
e7f0ad58
FB
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
87ecb68b 24#include "qemu-common.h"
28ecbaee 25#include "ui/console.h"
1de7afc9 26#include "qemu/timer.h"
ad39cf6d 27#include "qmp-commands.h"
927d4878 28#include "char/char.h"
e7f0ad58 29
6d6f7c28 30//#define DEBUG_CONSOLE
e7f0ad58
FB
31#define DEFAULT_BACKSCROLL 512
32#define MAX_CONSOLES 12
bf1bed81 33#define CONSOLE_CURSOR_PERIOD 500
e7f0ad58 34
26489844
FB
35#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
36#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
e7f0ad58 37
6d6f7c28
PB
38typedef struct TextAttributes {
39 uint8_t fgcol:4;
40 uint8_t bgcol:4;
41 uint8_t bold:1;
42 uint8_t uline:1;
43 uint8_t blink:1;
44 uint8_t invers:1;
45 uint8_t unvisible:1;
46} TextAttributes;
47
e7f0ad58
FB
48typedef struct TextCell {
49 uint8_t ch;
6d6f7c28 50 TextAttributes t_attrib;
e7f0ad58
FB
51} TextCell;
52
53#define MAX_ESC_PARAMS 3
54
55enum TTYState {
56 TTY_STATE_NORM,
57 TTY_STATE_ESC,
58 TTY_STATE_CSI,
59};
60
e15d7371
FB
61typedef struct QEMUFIFO {
62 uint8_t *buf;
63 int buf_size;
64 int count, wptr, rptr;
65} QEMUFIFO;
66
9596ebb7 67static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
e15d7371
FB
68{
69 int l, len;
70
71 l = f->buf_size - f->count;
72 if (len1 > l)
73 len1 = l;
74 len = len1;
75 while (len > 0) {
76 l = f->buf_size - f->wptr;
77 if (l > len)
78 l = len;
79 memcpy(f->buf + f->wptr, buf, l);
80 f->wptr += l;
81 if (f->wptr >= f->buf_size)
82 f->wptr = 0;
83 buf += l;
84 len -= l;
85 }
86 f->count += len1;
87 return len1;
88}
89
9596ebb7 90static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
e15d7371
FB
91{
92 int l, len;
93
94 if (len1 > f->count)
95 len1 = f->count;
96 len = len1;
97 while (len > 0) {
98 l = f->buf_size - f->rptr;
99 if (l > len)
100 l = len;
101 memcpy(buf, f->buf + f->rptr, l);
102 f->rptr += l;
103 if (f->rptr >= f->buf_size)
104 f->rptr = 0;
105 buf += l;
106 len -= l;
107 }
108 f->count -= len1;
109 return len1;
110}
111
af3a9031
TS
112typedef enum {
113 GRAPHIC_CONSOLE,
c21bbcfa
AZ
114 TEXT_CONSOLE,
115 TEXT_CONSOLE_FIXED_SIZE
c227f099 116} console_type_t;
af3a9031 117
76ffb0b4 118struct QemuConsole {
f81bdefb 119 int index;
c227f099 120 console_type_t console_type;
e7f0ad58 121 DisplayState *ds;
76ffb0b4 122
95219897
PB
123 /* Graphic console state. */
124 vga_hw_update_ptr hw_update;
125 vga_hw_invalidate_ptr hw_invalidate;
126 vga_hw_screen_dump_ptr hw_screen_dump;
4d3b6f6e 127 vga_hw_text_update_ptr hw_text_update;
95219897 128 void *hw;
e7f0ad58 129 int g_width, g_height;
76ffb0b4
GH
130
131 /* Text console state */
e7f0ad58
FB
132 int width;
133 int height;
134 int total_height;
135 int backscroll_height;
e7f0ad58 136 int x, y;
adb47967 137 int x_saved, y_saved;
e7f0ad58
FB
138 int y_displayed;
139 int y_base;
6d6f7c28
PB
140 TextAttributes t_attrib_default; /* default text attributes */
141 TextAttributes t_attrib; /* currently active text attributes */
e7f0ad58 142 TextCell *cells;
4d3b6f6e 143 int text_x[2], text_y[2], cursor_invalidate;
4104833f 144 int echo;
bf1bed81
JK
145 bool cursor_visible_phase;
146 QEMUTimer *cursor_timer;
e7f0ad58 147
14778c20
PB
148 int update_x0;
149 int update_y0;
150 int update_x1;
151 int update_y1;
152
e7f0ad58
FB
153 enum TTYState state;
154 int esc_params[MAX_ESC_PARAMS];
155 int nb_esc_params;
156
e5b0bc44 157 CharDriverState *chr;
e15d7371
FB
158 /* fifo for key pressed */
159 QEMUFIFO out_fifo;
160 uint8_t out_fifo_buf[16];
161 QEMUTimer *kbd_timer;
e7f0ad58
FB
162};
163
98b50080 164static DisplayState *display_state;
76ffb0b4
GH
165static QemuConsole *active_console;
166static QemuConsole *consoles[MAX_CONSOLES];
e7f0ad58
FB
167static int nb_consoles = 0;
168
95219897
PB
169void vga_hw_update(void)
170{
adb47967 171 if (active_console && active_console->hw_update)
95219897
PB
172 active_console->hw_update(active_console->hw);
173}
174
175void vga_hw_invalidate(void)
176{
26572b8a 177 if (active_console && active_console->hw_invalidate)
95219897
PB
178 active_console->hw_invalidate(active_console->hw);
179}
180
ad39cf6d 181void qmp_screendump(const char *filename, Error **errp)
95219897 182{
76ffb0b4 183 QemuConsole *previous_active_console;
45efb161 184 bool cswitch;
8571c055
AZ
185
186 previous_active_console = active_console;
45efb161 187 cswitch = previous_active_console && previous_active_console->index != 0;
f81bdefb 188
8571c055 189 /* There is currently no way of specifying which screen we want to dump,
7b455225 190 so always dump the first one. */
45efb161
GH
191 if (cswitch) {
192 console_select(0);
193 }
f81bdefb 194 if (consoles[0] && consoles[0]->hw_screen_dump) {
ad39cf6d 195 consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp);
16735102 196 } else {
312fd5f2 197 error_setg(errp, "device doesn't support screendump");
f81bdefb
JK
198 }
199
45efb161 200 if (cswitch) {
33bcd98c
AG
201 console_select(previous_active_console->index);
202 }
95219897
PB
203}
204
c227f099 205void vga_hw_text_update(console_ch_t *chardata)
4d3b6f6e
AZ
206{
207 if (active_console && active_console->hw_text_update)
208 active_console->hw_text_update(active_console->hw, chardata);
209}
210
1562e531
GH
211static void vga_fill_rect(QemuConsole *con,
212 int posx, int posy, int width, int height,
213 uint32_t color)
e7f0ad58 214{
1562e531 215 DisplaySurface *surface = qemu_console_surface(con);
e7f0ad58
FB
216 uint8_t *d, *d1;
217 int x, y, bpp;
3b46e624 218
1562e531
GH
219 bpp = surface_bytes_per_pixel(surface);
220 d1 = surface_data(surface) +
221 surface_stride(surface) * posy + bpp * posx;
e7f0ad58
FB
222 for (y = 0; y < height; y++) {
223 d = d1;
224 switch(bpp) {
225 case 1:
226 for (x = 0; x < width; x++) {
227 *((uint8_t *)d) = color;
228 d++;
229 }
230 break;
231 case 2:
232 for (x = 0; x < width; x++) {
233 *((uint16_t *)d) = color;
234 d += 2;
235 }
236 break;
237 case 4:
238 for (x = 0; x < width; x++) {
239 *((uint32_t *)d) = color;
240 d += 4;
241 }
242 break;
243 }
1562e531 244 d1 += surface_stride(surface);
e7f0ad58
FB
245 }
246}
247
248/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
1562e531
GH
249static void vga_bitblt(QemuConsole *con,
250 int xs, int ys, int xd, int yd, int w, int h)
e7f0ad58 251{
1562e531 252 DisplaySurface *surface = qemu_console_surface(con);
e7f0ad58
FB
253 const uint8_t *s;
254 uint8_t *d;
255 int wb, y, bpp;
256
1562e531 257 bpp = surface_bytes_per_pixel(surface);
e7f0ad58
FB
258 wb = w * bpp;
259 if (yd <= ys) {
1562e531
GH
260 s = surface_data(surface) +
261 surface_stride(surface) * ys + bpp * xs;
262 d = surface_data(surface) +
263 surface_stride(surface) * yd + bpp * xd;
e7f0ad58
FB
264 for (y = 0; y < h; y++) {
265 memmove(d, s, wb);
1562e531
GH
266 d += surface_stride(surface);
267 s += surface_stride(surface);
e7f0ad58
FB
268 }
269 } else {
1562e531
GH
270 s = surface_data(surface) +
271 surface_stride(surface) * (ys + h - 1) + bpp * xs;
272 d = surface_data(surface) +
273 surface_stride(surface) * (yd + h - 1) + bpp * xd;
e7f0ad58
FB
274 for (y = 0; y < h; y++) {
275 memmove(d, s, wb);
1562e531
GH
276 d -= surface_stride(surface);
277 s -= surface_stride(surface);
e7f0ad58
FB
278 }
279 }
280}
281
282/***********************************************************/
283/* basic char display */
284
285#define FONT_HEIGHT 16
286#define FONT_WIDTH 8
287
288#include "vgafont.h"
289
290#define cbswap_32(__x) \
291((uint32_t)( \
292 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
293 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
294 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
295 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
296
e2542fe2 297#ifdef HOST_WORDS_BIGENDIAN
e7f0ad58
FB
298#define PAT(x) x
299#else
300#define PAT(x) cbswap_32(x)
301#endif
302
303static const uint32_t dmask16[16] = {
304 PAT(0x00000000),
305 PAT(0x000000ff),
306 PAT(0x0000ff00),
307 PAT(0x0000ffff),
308 PAT(0x00ff0000),
309 PAT(0x00ff00ff),
310 PAT(0x00ffff00),
311 PAT(0x00ffffff),
312 PAT(0xff000000),
313 PAT(0xff0000ff),
314 PAT(0xff00ff00),
315 PAT(0xff00ffff),
316 PAT(0xffff0000),
317 PAT(0xffff00ff),
318 PAT(0xffffff00),
319 PAT(0xffffffff),
320};
321
322static const uint32_t dmask4[4] = {
323 PAT(0x00000000),
324 PAT(0x0000ffff),
325 PAT(0xffff0000),
326 PAT(0xffffffff),
327};
328
df00bed0 329#ifndef CONFIG_CURSES
6d6f7c28
PB
330enum color_names {
331 COLOR_BLACK = 0,
332 COLOR_RED = 1,
333 COLOR_GREEN = 2,
334 COLOR_YELLOW = 3,
335 COLOR_BLUE = 4,
336 COLOR_MAGENTA = 5,
337 COLOR_CYAN = 6,
338 COLOR_WHITE = 7
339};
df00bed0 340#endif
6d6f7c28
PB
341
342static const uint32_t color_table_rgb[2][8] = {
343 { /* dark */
26489844
FB
344 QEMU_RGB(0x00, 0x00, 0x00), /* black */
345 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
346 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
347 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
348 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
349 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
350 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
351 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
6d6f7c28
PB
352 },
353 { /* bright */
26489844
FB
354 QEMU_RGB(0x00, 0x00, 0x00), /* black */
355 QEMU_RGB(0xff, 0x00, 0x00), /* red */
356 QEMU_RGB(0x00, 0xff, 0x00), /* green */
357 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
358 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
359 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
360 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
361 QEMU_RGB(0xff, 0xff, 0xff), /* white */
6d6f7c28 362 }
e7f0ad58
FB
363};
364
6d6f7c28
PB
365#ifdef DEBUG_CONSOLE
366static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
367{
368 if (t_attrib->bold) {
369 printf("b");
370 } else {
371 printf(" ");
372 }
373 if (t_attrib->uline) {
374 printf("u");
375 } else {
376 printf(" ");
377 }
378 if (t_attrib->blink) {
379 printf("l");
380 } else {
381 printf(" ");
382 }
383 if (t_attrib->invers) {
384 printf("i");
385 } else {
386 printf(" ");
387 }
388 if (t_attrib->unvisible) {
389 printf("n");
390 } else {
391 printf(" ");
392 }
393
394 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
395}
396#endif
e7f0ad58 397
1562e531 398static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
6d6f7c28 399 TextAttributes *t_attrib)
e7f0ad58 400{
1562e531 401 DisplaySurface *surface = qemu_console_surface(s);
e7f0ad58
FB
402 uint8_t *d;
403 const uint8_t *font_ptr;
404 unsigned int font_data, linesize, xorcol, bpp;
405 int i;
6d6f7c28
PB
406 unsigned int fgcol, bgcol;
407
408#ifdef DEBUG_CONSOLE
409 printf("x: %2i y: %2i", x, y);
410 console_print_text_attributes(t_attrib, ch);
411#endif
412
413 if (t_attrib->invers) {
cf6f0548
GH
414 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
415 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
6d6f7c28 416 } else {
cf6f0548
GH
417 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
418 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
6d6f7c28 419 }
e7f0ad58 420
1562e531
GH
421 bpp = surface_bytes_per_pixel(surface);
422 d = surface_data(surface) +
423 surface_stride(surface) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
424 linesize = surface_stride(surface);
e7f0ad58
FB
425 font_ptr = vgafont16 + FONT_HEIGHT * ch;
426 xorcol = bgcol ^ fgcol;
1562e531 427 switch (surface_bits_per_pixel(surface)) {
e7f0ad58
FB
428 case 8:
429 for(i = 0; i < FONT_HEIGHT; i++) {
430 font_data = *font_ptr++;
6d6f7c28
PB
431 if (t_attrib->uline
432 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
439229c7 433 font_data = 0xFF;
6d6f7c28 434 }
e7f0ad58
FB
435 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
436 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
437 d += linesize;
438 }
439 break;
440 case 16:
441 case 15:
442 for(i = 0; i < FONT_HEIGHT; i++) {
443 font_data = *font_ptr++;
6d6f7c28
PB
444 if (t_attrib->uline
445 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
439229c7 446 font_data = 0xFF;
6d6f7c28 447 }
e7f0ad58
FB
448 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
449 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
450 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
451 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
452 d += linesize;
453 }
454 break;
455 case 32:
456 for(i = 0; i < FONT_HEIGHT; i++) {
457 font_data = *font_ptr++;
6d6f7c28 458 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
439229c7 459 font_data = 0xFF;
6d6f7c28 460 }
e7f0ad58
FB
461 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
462 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
463 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
464 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
465 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
466 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
467 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
468 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
469 d += linesize;
470 }
471 break;
472 }
473}
474
76ffb0b4 475static void text_console_resize(QemuConsole *s)
e7f0ad58
FB
476{
477 TextCell *cells, *c, *c1;
478 int w1, x, y, last_width;
479
480 last_width = s->width;
481 s->width = s->g_width / FONT_WIDTH;
482 s->height = s->g_height / FONT_HEIGHT;
483
484 w1 = last_width;
485 if (s->width < w1)
486 w1 = s->width;
487
7267c094 488 cells = g_malloc(s->width * s->total_height * sizeof(TextCell));
e7f0ad58
FB
489 for(y = 0; y < s->total_height; y++) {
490 c = &cells[y * s->width];
491 if (w1 > 0) {
492 c1 = &s->cells[y * last_width];
493 for(x = 0; x < w1; x++) {
494 *c++ = *c1++;
495 }
496 }
497 for(x = w1; x < s->width; x++) {
498 c->ch = ' ';
6d6f7c28 499 c->t_attrib = s->t_attrib_default;
e7f0ad58
FB
500 c++;
501 }
502 }
7267c094 503 g_free(s->cells);
e7f0ad58
FB
504 s->cells = cells;
505}
506
76ffb0b4 507static inline void text_update_xy(QemuConsole *s, int x, int y)
4d3b6f6e
AZ
508{
509 s->text_x[0] = MIN(s->text_x[0], x);
510 s->text_x[1] = MAX(s->text_x[1], x);
511 s->text_y[0] = MIN(s->text_y[0], y);
512 s->text_y[1] = MAX(s->text_y[1], y);
513}
514
76ffb0b4 515static void invalidate_xy(QemuConsole *s, int x, int y)
14778c20
PB
516{
517 if (s->update_x0 > x * FONT_WIDTH)
518 s->update_x0 = x * FONT_WIDTH;
519 if (s->update_y0 > y * FONT_HEIGHT)
520 s->update_y0 = y * FONT_HEIGHT;
521 if (s->update_x1 < (x + 1) * FONT_WIDTH)
522 s->update_x1 = (x + 1) * FONT_WIDTH;
523 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
524 s->update_y1 = (y + 1) * FONT_HEIGHT;
525}
526
76ffb0b4 527static void update_xy(QemuConsole *s, int x, int y)
e7f0ad58
FB
528{
529 TextCell *c;
530 int y1, y2;
531
1562e531
GH
532 if (s != active_console) {
533 return;
534 }
535
536 if (s->ds->have_text) {
537 text_update_xy(s, x, y);
538 }
4d3b6f6e 539
1562e531 540 if (s->ds->have_gfx) {
e7f0ad58
FB
541 y1 = (s->y_base + y) % s->total_height;
542 y2 = y1 - s->y_displayed;
543 if (y2 < 0)
544 y2 += s->total_height;
545 if (y2 < s->height) {
546 c = &s->cells[y1 * s->width + x];
1562e531 547 vga_putcharxy(s, x, y2, c->ch,
6d6f7c28 548 &(c->t_attrib));
14778c20 549 invalidate_xy(s, x, y2);
e7f0ad58
FB
550 }
551 }
552}
553
76ffb0b4 554static void console_show_cursor(QemuConsole *s, int show)
e7f0ad58
FB
555{
556 TextCell *c;
557 int y, y1;
1562e531 558 int x = s->x;
e7f0ad58 559
1562e531
GH
560 if (s != active_console) {
561 return;
562 }
4d3b6f6e 563
1562e531
GH
564 if (s->ds->have_text) {
565 s->cursor_invalidate = 1;
566 }
4d3b6f6e 567
1562e531 568 if (s->ds->have_gfx) {
ed8276ac
TS
569 if (x >= s->width) {
570 x = s->width - 1;
571 }
e7f0ad58
FB
572 y1 = (s->y_base + s->y) % s->total_height;
573 y = y1 - s->y_displayed;
574 if (y < 0)
575 y += s->total_height;
576 if (y < s->height) {
ed8276ac 577 c = &s->cells[y1 * s->width + x];
bf1bed81 578 if (show && s->cursor_visible_phase) {
6d6f7c28
PB
579 TextAttributes t_attrib = s->t_attrib_default;
580 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
1562e531 581 vga_putcharxy(s, x, y, c->ch, &t_attrib);
e7f0ad58 582 } else {
1562e531 583 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
e7f0ad58 584 }
14778c20 585 invalidate_xy(s, x, y);
e7f0ad58
FB
586 }
587 }
588}
589
76ffb0b4 590static void console_refresh(QemuConsole *s)
e7f0ad58 591{
1562e531 592 DisplaySurface *surface = qemu_console_surface(s);
e7f0ad58
FB
593 TextCell *c;
594 int x, y, y1;
595
5fafdf24 596 if (s != active_console)
e7f0ad58 597 return;
a93a4a22
GH
598
599 if (s->ds->have_text) {
4d3b6f6e
AZ
600 s->text_x[0] = 0;
601 s->text_y[0] = 0;
602 s->text_x[1] = s->width - 1;
603 s->text_y[1] = s->height - 1;
604 s->cursor_invalidate = 1;
4d3b6f6e 605 }
e7f0ad58 606
a93a4a22 607 if (s->ds->have_gfx) {
1562e531 608 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
cf6f0548 609 color_table_rgb[0][COLOR_BLACK]);
a93a4a22
GH
610 y1 = s->y_displayed;
611 for (y = 0; y < s->height; y++) {
612 c = s->cells + y1 * s->width;
613 for (x = 0; x < s->width; x++) {
1562e531 614 vga_putcharxy(s, x, y, c->ch,
a93a4a22
GH
615 &(c->t_attrib));
616 c++;
617 }
618 if (++y1 == s->total_height) {
619 y1 = 0;
620 }
e7f0ad58 621 }
a93a4a22 622 console_show_cursor(s, 1);
1562e531
GH
623 dpy_gfx_update(s, 0, 0,
624 surface_width(surface), surface_height(surface));
e7f0ad58 625 }
e7f0ad58
FB
626}
627
628static void console_scroll(int ydelta)
629{
76ffb0b4 630 QemuConsole *s;
e7f0ad58 631 int i, y1;
3b46e624 632
e7f0ad58 633 s = active_console;
af3a9031 634 if (!s || (s->console_type == GRAPHIC_CONSOLE))
e7f0ad58
FB
635 return;
636
637 if (ydelta > 0) {
638 for(i = 0; i < ydelta; i++) {
639 if (s->y_displayed == s->y_base)
640 break;
641 if (++s->y_displayed == s->total_height)
642 s->y_displayed = 0;
643 }
644 } else {
645 ydelta = -ydelta;
646 i = s->backscroll_height;
647 if (i > s->total_height - s->height)
648 i = s->total_height - s->height;
649 y1 = s->y_base - i;
650 if (y1 < 0)
651 y1 += s->total_height;
652 for(i = 0; i < ydelta; i++) {
653 if (s->y_displayed == y1)
654 break;
655 if (--s->y_displayed < 0)
656 s->y_displayed = s->total_height - 1;
657 }
658 }
659 console_refresh(s);
660}
661
76ffb0b4 662static void console_put_lf(QemuConsole *s)
e7f0ad58
FB
663{
664 TextCell *c;
665 int x, y1;
666
e7f0ad58
FB
667 s->y++;
668 if (s->y >= s->height) {
669 s->y = s->height - 1;
6d6f7c28 670
e7f0ad58
FB
671 if (s->y_displayed == s->y_base) {
672 if (++s->y_displayed == s->total_height)
673 s->y_displayed = 0;
674 }
675 if (++s->y_base == s->total_height)
676 s->y_base = 0;
677 if (s->backscroll_height < s->total_height)
678 s->backscroll_height++;
679 y1 = (s->y_base + s->height - 1) % s->total_height;
680 c = &s->cells[y1 * s->width];
681 for(x = 0; x < s->width; x++) {
682 c->ch = ' ';
6d6f7c28 683 c->t_attrib = s->t_attrib_default;
e7f0ad58
FB
684 c++;
685 }
686 if (s == active_console && s->y_displayed == s->y_base) {
1562e531 687 if (s->ds->have_text) {
4d3b6f6e
AZ
688 s->text_x[0] = 0;
689 s->text_y[0] = 0;
690 s->text_x[1] = s->width - 1;
691 s->text_y[1] = s->height - 1;
4d3b6f6e
AZ
692 }
693
1562e531
GH
694 if (s->ds->have_gfx) {
695 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
696 s->width * FONT_WIDTH,
697 (s->height - 1) * FONT_HEIGHT);
698 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
699 s->width * FONT_WIDTH, FONT_HEIGHT,
700 color_table_rgb[0][s->t_attrib_default.bgcol]);
701 s->update_x0 = 0;
702 s->update_y0 = 0;
703 s->update_x1 = s->width * FONT_WIDTH;
704 s->update_y1 = s->height * FONT_HEIGHT;
705 }
e7f0ad58
FB
706 }
707 }
708}
709
6d6f7c28
PB
710/* Set console attributes depending on the current escape codes.
711 * NOTE: I know this code is not very efficient (checking every color for it
712 * self) but it is more readable and better maintainable.
713 */
76ffb0b4 714static void console_handle_escape(QemuConsole *s)
6d6f7c28
PB
715{
716 int i;
717
6d6f7c28
PB
718 for (i=0; i<s->nb_esc_params; i++) {
719 switch (s->esc_params[i]) {
720 case 0: /* reset all console attributes to default */
721 s->t_attrib = s->t_attrib_default;
722 break;
723 case 1:
724 s->t_attrib.bold = 1;
725 break;
726 case 4:
727 s->t_attrib.uline = 1;
728 break;
729 case 5:
730 s->t_attrib.blink = 1;
731 break;
732 case 7:
733 s->t_attrib.invers = 1;
734 break;
735 case 8:
736 s->t_attrib.unvisible = 1;
737 break;
738 case 22:
739 s->t_attrib.bold = 0;
740 break;
741 case 24:
742 s->t_attrib.uline = 0;
743 break;
744 case 25:
745 s->t_attrib.blink = 0;
746 break;
747 case 27:
748 s->t_attrib.invers = 0;
749 break;
750 case 28:
751 s->t_attrib.unvisible = 0;
752 break;
753 /* set foreground color */
754 case 30:
755 s->t_attrib.fgcol=COLOR_BLACK;
756 break;
757 case 31:
758 s->t_attrib.fgcol=COLOR_RED;
759 break;
760 case 32:
761 s->t_attrib.fgcol=COLOR_GREEN;
762 break;
763 case 33:
764 s->t_attrib.fgcol=COLOR_YELLOW;
765 break;
766 case 34:
767 s->t_attrib.fgcol=COLOR_BLUE;
768 break;
769 case 35:
770 s->t_attrib.fgcol=COLOR_MAGENTA;
771 break;
772 case 36:
773 s->t_attrib.fgcol=COLOR_CYAN;
774 break;
775 case 37:
776 s->t_attrib.fgcol=COLOR_WHITE;
777 break;
778 /* set background color */
779 case 40:
780 s->t_attrib.bgcol=COLOR_BLACK;
781 break;
782 case 41:
783 s->t_attrib.bgcol=COLOR_RED;
784 break;
785 case 42:
786 s->t_attrib.bgcol=COLOR_GREEN;
787 break;
788 case 43:
789 s->t_attrib.bgcol=COLOR_YELLOW;
790 break;
791 case 44:
792 s->t_attrib.bgcol=COLOR_BLUE;
793 break;
794 case 45:
795 s->t_attrib.bgcol=COLOR_MAGENTA;
796 break;
797 case 46:
798 s->t_attrib.bgcol=COLOR_CYAN;
799 break;
800 case 47:
801 s->t_attrib.bgcol=COLOR_WHITE;
802 break;
803 }
804 }
805}
806
76ffb0b4 807static void console_clear_xy(QemuConsole *s, int x, int y)
adb47967
TS
808{
809 int y1 = (s->y_base + y) % s->total_height;
810 TextCell *c = &s->cells[y1 * s->width + x];
811 c->ch = ' ';
812 c->t_attrib = s->t_attrib_default;
adb47967
TS
813 update_xy(s, x, y);
814}
815
3eea5498 816/* set cursor, checking bounds */
76ffb0b4 817static void set_cursor(QemuConsole *s, int x, int y)
3eea5498
IC
818{
819 if (x < 0) {
820 x = 0;
821 }
822 if (y < 0) {
823 y = 0;
824 }
825 if (y >= s->height) {
826 y = s->height - 1;
827 }
828 if (x >= s->width) {
829 x = s->width - 1;
830 }
831
832 s->x = x;
833 s->y = y;
834}
835
76ffb0b4 836static void console_putchar(QemuConsole *s, int ch)
e7f0ad58
FB
837{
838 TextCell *c;
adb47967
TS
839 int y1, i;
840 int x, y;
e7f0ad58
FB
841
842 switch(s->state) {
843 case TTY_STATE_NORM:
844 switch(ch) {
6d6f7c28 845 case '\r': /* carriage return */
e7f0ad58
FB
846 s->x = 0;
847 break;
6d6f7c28 848 case '\n': /* newline */
e7f0ad58
FB
849 console_put_lf(s);
850 break;
6d6f7c28 851 case '\b': /* backspace */
5fafdf24 852 if (s->x > 0)
e15d7371 853 s->x--;
6d6f7c28
PB
854 break;
855 case '\t': /* tabspace */
856 if (s->x + (8 - (s->x % 8)) > s->width) {
bd468840 857 s->x = 0;
6d6f7c28
PB
858 console_put_lf(s);
859 } else {
860 s->x = s->x + (8 - (s->x % 8));
861 }
862 break;
863 case '\a': /* alert aka. bell */
864 /* TODO: has to be implemented */
865 break;
adb47967
TS
866 case 14:
867 /* SI (shift in), character set 0 (ignored) */
868 break;
869 case 15:
870 /* SO (shift out), character set 1 (ignored) */
871 break;
6d6f7c28 872 case 27: /* esc (introducing an escape sequence) */
e7f0ad58
FB
873 s->state = TTY_STATE_ESC;
874 break;
875 default:
ed8276ac
TS
876 if (s->x >= s->width) {
877 /* line wrap */
878 s->x = 0;
879 console_put_lf(s);
adb47967 880 }
e7f0ad58
FB
881 y1 = (s->y_base + s->y) % s->total_height;
882 c = &s->cells[y1 * s->width + s->x];
883 c->ch = ch;
6d6f7c28 884 c->t_attrib = s->t_attrib;
e7f0ad58
FB
885 update_xy(s, s->x, s->y);
886 s->x++;
e7f0ad58
FB
887 break;
888 }
889 break;
6d6f7c28 890 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
e7f0ad58
FB
891 if (ch == '[') {
892 for(i=0;i<MAX_ESC_PARAMS;i++)
893 s->esc_params[i] = 0;
894 s->nb_esc_params = 0;
895 s->state = TTY_STATE_CSI;
896 } else {
897 s->state = TTY_STATE_NORM;
898 }
899 break;
6d6f7c28 900 case TTY_STATE_CSI: /* handle escape sequence parameters */
e7f0ad58
FB
901 if (ch >= '0' && ch <= '9') {
902 if (s->nb_esc_params < MAX_ESC_PARAMS) {
c10600af
LE
903 int *param = &s->esc_params[s->nb_esc_params];
904 int digit = (ch - '0');
905
906 *param = (*param <= (INT_MAX - digit) / 10) ?
907 *param * 10 + digit : INT_MAX;
e7f0ad58
FB
908 }
909 } else {
3eea5498
IC
910 if (s->nb_esc_params < MAX_ESC_PARAMS)
911 s->nb_esc_params++;
e7f0ad58
FB
912 if (ch == ';')
913 break;
adb47967
TS
914#ifdef DEBUG_CONSOLE
915 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
916 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
917#endif
e7f0ad58
FB
918 s->state = TTY_STATE_NORM;
919 switch(ch) {
adb47967
TS
920 case 'A':
921 /* move cursor up */
922 if (s->esc_params[0] == 0) {
923 s->esc_params[0] = 1;
924 }
3eea5498 925 set_cursor(s, s->x, s->y - s->esc_params[0]);
adb47967
TS
926 break;
927 case 'B':
928 /* move cursor down */
929 if (s->esc_params[0] == 0) {
930 s->esc_params[0] = 1;
931 }
3eea5498 932 set_cursor(s, s->x, s->y + s->esc_params[0]);
e7f0ad58
FB
933 break;
934 case 'C':
adb47967
TS
935 /* move cursor right */
936 if (s->esc_params[0] == 0) {
937 s->esc_params[0] = 1;
938 }
3eea5498 939 set_cursor(s, s->x + s->esc_params[0], s->y);
e7f0ad58 940 break;
adb47967
TS
941 case 'D':
942 /* move cursor left */
943 if (s->esc_params[0] == 0) {
944 s->esc_params[0] = 1;
945 }
3eea5498 946 set_cursor(s, s->x - s->esc_params[0], s->y);
adb47967
TS
947 break;
948 case 'G':
949 /* move cursor to column */
3eea5498 950 set_cursor(s, s->esc_params[0] - 1, s->y);
adb47967
TS
951 break;
952 case 'f':
953 case 'H':
954 /* move cursor to row, column */
3eea5498 955 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
adb47967
TS
956 break;
957 case 'J':
958 switch (s->esc_params[0]) {
959 case 0:
960 /* clear to end of screen */
961 for (y = s->y; y < s->height; y++) {
962 for (x = 0; x < s->width; x++) {
963 if (y == s->y && x < s->x) {
964 continue;
965 }
966 console_clear_xy(s, x, y);
967 }
968 }
969 break;
970 case 1:
971 /* clear from beginning of screen */
972 for (y = 0; y <= s->y; y++) {
973 for (x = 0; x < s->width; x++) {
974 if (y == s->y && x > s->x) {
975 break;
976 }
977 console_clear_xy(s, x, y);
978 }
979 }
980 break;
981 case 2:
982 /* clear entire screen */
983 for (y = 0; y <= s->height; y++) {
984 for (x = 0; x < s->width; x++) {
985 console_clear_xy(s, x, y);
986 }
987 }
f94a950f 988 break;
adb47967 989 }
95d8f9f4 990 break;
e7f0ad58 991 case 'K':
adb47967
TS
992 switch (s->esc_params[0]) {
993 case 0:
f94a950f
MA
994 /* clear to eol */
995 for(x = s->x; x < s->width; x++) {
adb47967 996 console_clear_xy(s, x, s->y);
f94a950f
MA
997 }
998 break;
adb47967
TS
999 case 1:
1000 /* clear from beginning of line */
1001 for (x = 0; x <= s->x; x++) {
1002 console_clear_xy(s, x, s->y);
1003 }
1004 break;
1005 case 2:
1006 /* clear entire line */
1007 for(x = 0; x < s->width; x++) {
1008 console_clear_xy(s, x, s->y);
1009 }
f94a950f
MA
1010 break;
1011 }
adb47967
TS
1012 break;
1013 case 'm':
f94a950f
MA
1014 console_handle_escape(s);
1015 break;
adb47967
TS
1016 case 'n':
1017 /* report cursor position */
1018 /* TODO: send ESC[row;colR */
1019 break;
1020 case 's':
1021 /* save cursor position */
1022 s->x_saved = s->x;
1023 s->y_saved = s->y;
1024 break;
1025 case 'u':
1026 /* restore cursor position */
1027 s->x = s->x_saved;
1028 s->y = s->y_saved;
1029 break;
1030 default:
1031#ifdef DEBUG_CONSOLE
1032 fprintf(stderr, "unhandled escape character '%c'\n", ch);
1033#endif
1034 break;
1035 }
1036 break;
e7f0ad58
FB
1037 }
1038 }
1039}
1040
1041void console_select(unsigned int index)
1042{
1562e531 1043 DisplaySurface *surface;
76ffb0b4 1044 QemuConsole *s;
6d6f7c28 1045
e7f0ad58
FB
1046 if (index >= MAX_CONSOLES)
1047 return;
358664cc 1048 if (active_console) {
1562e531
GH
1049 surface = qemu_console_surface(active_console);
1050 active_console->g_width = surface_width(surface);
1051 active_console->g_height = surface_height(surface);
358664cc 1052 }
e7f0ad58
FB
1053 s = consoles[index];
1054 if (s) {
7d957bd8 1055 DisplayState *ds = s->ds;
bf1bed81 1056
8bd6b06d 1057 if (active_console && active_console->cursor_timer) {
bf1bed81
JK
1058 qemu_del_timer(active_console->cursor_timer);
1059 }
e7f0ad58 1060 active_console = s;
a93a4a22 1061 if (ds->have_gfx) {
da229ef3 1062 surface = qemu_create_displaysurface(s->g_width, s->g_height);
c78f7137 1063 dpy_gfx_replace_surface(s, surface);
a93a4a22
GH
1064 }
1065 if (ds->have_text) {
c78f7137 1066 dpy_text_resize(s, s->width, s->height);
68f00996 1067 }
bf1bed81
JK
1068 if (s->cursor_timer) {
1069 qemu_mod_timer(s->cursor_timer,
1070 qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1071 }
4d3b6f6e 1072 vga_hw_invalidate();
e7f0ad58
FB
1073 }
1074}
1075
1076static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1077{
76ffb0b4 1078 QemuConsole *s = chr->opaque;
e7f0ad58
FB
1079 int i;
1080
14778c20
PB
1081 s->update_x0 = s->width * FONT_WIDTH;
1082 s->update_y0 = s->height * FONT_HEIGHT;
1083 s->update_x1 = 0;
1084 s->update_y1 = 0;
e7f0ad58
FB
1085 console_show_cursor(s, 0);
1086 for(i = 0; i < len; i++) {
1087 console_putchar(s, buf[i]);
1088 }
1089 console_show_cursor(s, 1);
a93a4a22 1090 if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
c78f7137 1091 dpy_gfx_update(s, s->update_x0, s->update_y0,
a93a4a22
GH
1092 s->update_x1 - s->update_x0,
1093 s->update_y1 - s->update_y0);
14778c20 1094 }
e7f0ad58
FB
1095 return len;
1096}
1097
e15d7371
FB
1098static void kbd_send_chars(void *opaque)
1099{
76ffb0b4 1100 QemuConsole *s = opaque;
e15d7371
FB
1101 int len;
1102 uint8_t buf[16];
3b46e624 1103
909cda12 1104 len = qemu_chr_be_can_write(s->chr);
e15d7371
FB
1105 if (len > s->out_fifo.count)
1106 len = s->out_fifo.count;
1107 if (len > 0) {
1108 if (len > sizeof(buf))
1109 len = sizeof(buf);
1110 qemu_fifo_read(&s->out_fifo, buf, len);
fa5efccb 1111 qemu_chr_be_write(s->chr, buf, len);
e15d7371
FB
1112 }
1113 /* characters are pending: we send them a bit later (XXX:
1114 horrible, should change char device API) */
1115 if (s->out_fifo.count > 0) {
7bd427d8 1116 qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
e15d7371
FB
1117 }
1118}
1119
e7f0ad58
FB
1120/* called when an ascii key is pressed */
1121void kbd_put_keysym(int keysym)
1122{
76ffb0b4 1123 QemuConsole *s;
e7f0ad58
FB
1124 uint8_t buf[16], *q;
1125 int c;
1126
1127 s = active_console;
af3a9031 1128 if (!s || (s->console_type == GRAPHIC_CONSOLE))
e7f0ad58
FB
1129 return;
1130
1131 switch(keysym) {
1132 case QEMU_KEY_CTRL_UP:
1133 console_scroll(-1);
1134 break;
1135 case QEMU_KEY_CTRL_DOWN:
1136 console_scroll(1);
1137 break;
1138 case QEMU_KEY_CTRL_PAGEUP:
1139 console_scroll(-10);
1140 break;
1141 case QEMU_KEY_CTRL_PAGEDOWN:
1142 console_scroll(10);
1143 break;
1144 default:
e15d7371
FB
1145 /* convert the QEMU keysym to VT100 key string */
1146 q = buf;
1147 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1148 *q++ = '\033';
1149 *q++ = '[';
1150 c = keysym - 0xe100;
1151 if (c >= 10)
1152 *q++ = '0' + (c / 10);
1153 *q++ = '0' + (c % 10);
1154 *q++ = '~';
1155 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1156 *q++ = '\033';
1157 *q++ = '[';
1158 *q++ = keysym & 0xff;
4104833f
PB
1159 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1160 console_puts(s->chr, (const uint8_t *) "\r", 1);
1161 *q++ = '\n';
e15d7371 1162 } else {
4104833f
PB
1163 *q++ = keysym;
1164 }
1165 if (s->echo) {
1166 console_puts(s->chr, buf, q - buf);
e15d7371 1167 }
e5b0bc44 1168 if (s->chr->chr_read) {
e15d7371
FB
1169 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1170 kbd_send_chars(s);
e7f0ad58
FB
1171 }
1172 break;
1173 }
1174}
1175
4d3b6f6e
AZ
1176static void text_console_invalidate(void *opaque)
1177{
76ffb0b4 1178 QemuConsole *s = (QemuConsole *) opaque;
1562e531
GH
1179 DisplaySurface *surface = qemu_console_surface(s);
1180
1181 if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1182 s->g_width = surface_width(surface);
1183 s->g_height = surface_height(surface);
68f00996
AL
1184 text_console_resize(s);
1185 }
4d3b6f6e
AZ
1186 console_refresh(s);
1187}
1188
c227f099 1189static void text_console_update(void *opaque, console_ch_t *chardata)
4d3b6f6e 1190{
76ffb0b4 1191 QemuConsole *s = (QemuConsole *) opaque;
4d3b6f6e
AZ
1192 int i, j, src;
1193
1194 if (s->text_x[0] <= s->text_x[1]) {
1195 src = (s->y_base + s->text_y[0]) * s->width;
1196 chardata += s->text_y[0] * s->width;
1197 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1198 for (j = 0; j < s->width; j ++, src ++)
1199 console_write_ch(chardata ++, s->cells[src].ch |
1200 (s->cells[src].t_attrib.fgcol << 12) |
1201 (s->cells[src].t_attrib.bgcol << 8) |
1202 (s->cells[src].t_attrib.bold << 21));
c78f7137 1203 dpy_text_update(s, s->text_x[0], s->text_y[0],
a93a4a22 1204 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
4d3b6f6e
AZ
1205 s->text_x[0] = s->width;
1206 s->text_y[0] = s->height;
1207 s->text_x[1] = 0;
1208 s->text_y[1] = 0;
1209 }
1210 if (s->cursor_invalidate) {
c78f7137 1211 dpy_text_cursor(s, s->x, s->y);
4d3b6f6e
AZ
1212 s->cursor_invalidate = 0;
1213 }
1214}
1215
76ffb0b4 1216static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
e7f0ad58 1217{
76ffb0b4 1218 QemuConsole *s;
95219897 1219 int i;
e7f0ad58
FB
1220
1221 if (nb_consoles >= MAX_CONSOLES)
1222 return NULL;
76ffb0b4 1223 s = g_malloc0(sizeof(QemuConsole));
af3a9031
TS
1224 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1225 (console_type == GRAPHIC_CONSOLE))) {
e7f0ad58 1226 active_console = s;
af3a9031 1227 }
e7f0ad58 1228 s->ds = ds;
af3a9031
TS
1229 s->console_type = console_type;
1230 if (console_type != GRAPHIC_CONSOLE) {
f81bdefb 1231 s->index = nb_consoles;
95219897
PB
1232 consoles[nb_consoles++] = s;
1233 } else {
1234 /* HACK: Put graphical consoles before text consoles. */
1235 for (i = nb_consoles; i > 0; i--) {
af3a9031 1236 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
95219897
PB
1237 break;
1238 consoles[i] = consoles[i - 1];
f81bdefb 1239 consoles[i]->index = i;
95219897 1240 }
f81bdefb 1241 s->index = i;
95219897 1242 consoles[i] = s;
3023f332 1243 nb_consoles++;
95219897
PB
1244 }
1245 return s;
1246}
1247
537a4391
GH
1248static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1249 int linesize, PixelFormat pf, int newflags)
ffe8b821 1250{
ffe8b821 1251 surface->pf = pf;
69c77777
GH
1252
1253 qemu_pixman_image_unref(surface->image);
1254 surface->image = NULL;
69c77777
GH
1255
1256 surface->format = qemu_pixman_get_format(&pf);
1257 assert(surface->format != 0);
1258 surface->image = pixman_image_create_bits(surface->format,
1259 width, height,
1260 NULL, linesize);
1261 assert(surface->image != NULL);
1262
ffe8b821 1263 surface->flags = newflags | QEMU_ALLOCATED_FLAG;
98b50080 1264#ifdef HOST_WORDS_BIGENDIAN
ffe8b821 1265 surface->flags |= QEMU_BIG_ENDIAN_FLAG;
98b50080 1266#endif
98b50080
PB
1267}
1268
da229ef3 1269DisplaySurface *qemu_create_displaysurface(int width, int height)
537a4391
GH
1270{
1271 DisplaySurface *surface = g_new0(DisplaySurface, 1);
537a4391 1272 int linesize = width * 4;
da229ef3
GH
1273
1274 trace_displaysurface_create(surface, width, height);
537a4391
GH
1275 qemu_alloc_display(surface, width, height, linesize,
1276 qemu_default_pixelformat(32), 0);
1277 return surface;
1278}
1279
187cd1d9 1280DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
b1424e03
GH
1281 int linesize, uint8_t *data,
1282 bool byteswap)
98b50080 1283{
69c77777 1284 DisplaySurface *surface = g_new0(DisplaySurface, 1);
98b50080 1285
da229ef3 1286 trace_displaysurface_create_from(surface, width, height, bpp, byteswap);
b1424e03
GH
1287 if (byteswap) {
1288 surface->pf = qemu_different_endianness_pixelformat(bpp);
1289 } else {
1290 surface->pf = qemu_default_pixelformat(bpp);
1291 }
69c77777
GH
1292
1293 surface->format = qemu_pixman_get_format(&surface->pf);
1294 assert(surface->format != 0);
1295 surface->image = pixman_image_create_bits(surface->format,
1296 width, height,
1297 (void *)data, linesize);
1298 assert(surface->image != NULL);
1299
98b50080
PB
1300#ifdef HOST_WORDS_BIGENDIAN
1301 surface->flags = QEMU_BIG_ENDIAN_FLAG;
1302#endif
98b50080
PB
1303
1304 return surface;
1305}
1306
da229ef3 1307void qemu_free_displaysurface(DisplaySurface *surface)
98b50080 1308{
da229ef3 1309 if (surface == NULL) {
98b50080 1310 return;
187cd1d9 1311 }
da229ef3
GH
1312 trace_displaysurface_free(surface);
1313 qemu_pixman_image_unref(surface->image);
1314 g_free(surface);
98b50080
PB
1315}
1316
7c20b4a3
GH
1317void register_displaychangelistener(DisplayState *ds,
1318 DisplayChangeListener *dcl)
1319{
1320 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1321 dcl->ds = ds;
1322 QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
1323 gui_setup_refresh(ds);
c12aeb86 1324 if (dcl->ops->dpy_gfx_switch) {
bc2ed970 1325 dcl->ops->dpy_gfx_switch(dcl, ds->surface);
7c20b4a3
GH
1326 }
1327}
1328
1329void unregister_displaychangelistener(DisplayChangeListener *dcl)
1330{
1331 DisplayState *ds = dcl->ds;
1332 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1333 QLIST_REMOVE(dcl, next);
1334 gui_setup_refresh(ds);
1335}
1336
c78f7137 1337void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
7c20b4a3 1338{
c78f7137 1339 DisplayState *s = con->ds;
7c20b4a3
GH
1340 struct DisplayChangeListener *dcl;
1341 int width = pixman_image_get_width(s->surface->image);
1342 int height = pixman_image_get_height(s->surface->image);
1343
1344 x = MAX(x, 0);
1345 y = MAX(y, 0);
1346 x = MIN(x, width);
1347 y = MIN(y, height);
1348 w = MIN(w, width - x);
1349 h = MIN(h, height - y);
1350
1351 QLIST_FOREACH(dcl, &s->listeners, next) {
1352 if (dcl->ops->dpy_gfx_update) {
bc2ed970 1353 dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
7c20b4a3
GH
1354 }
1355 }
1356}
1357
c78f7137 1358void dpy_gfx_replace_surface(QemuConsole *con,
da229ef3 1359 DisplaySurface *surface)
7c20b4a3 1360{
c78f7137 1361 DisplayState *s = con->ds;
da229ef3 1362 DisplaySurface *old_surface = s->surface;
7c20b4a3 1363 struct DisplayChangeListener *dcl;
da229ef3
GH
1364
1365 s->surface = surface;
7c20b4a3 1366 QLIST_FOREACH(dcl, &s->listeners, next) {
c12aeb86 1367 if (dcl->ops->dpy_gfx_switch) {
bc2ed970 1368 dcl->ops->dpy_gfx_switch(dcl, surface);
7c20b4a3
GH
1369 }
1370 }
da229ef3 1371 qemu_free_displaysurface(old_surface);
7c20b4a3
GH
1372}
1373
1374void dpy_refresh(DisplayState *s)
1375{
1376 struct DisplayChangeListener *dcl;
1377 QLIST_FOREACH(dcl, &s->listeners, next) {
1378 if (dcl->ops->dpy_refresh) {
bc2ed970 1379 dcl->ops->dpy_refresh(dcl);
7c20b4a3
GH
1380 }
1381 }
1382}
1383
c78f7137
GH
1384void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
1385 int dst_x, int dst_y, int w, int h)
7c20b4a3 1386{
c78f7137 1387 DisplayState *s = con->ds;
7c20b4a3
GH
1388 struct DisplayChangeListener *dcl;
1389 QLIST_FOREACH(dcl, &s->listeners, next) {
1390 if (dcl->ops->dpy_gfx_copy) {
bc2ed970 1391 dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
7c20b4a3 1392 } else { /* TODO */
bc2ed970 1393 dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
7c20b4a3
GH
1394 }
1395 }
1396}
1397
c78f7137 1398void dpy_text_cursor(QemuConsole *con, int x, int y)
7c20b4a3 1399{
c78f7137 1400 DisplayState *s = con->ds;
7c20b4a3
GH
1401 struct DisplayChangeListener *dcl;
1402 QLIST_FOREACH(dcl, &s->listeners, next) {
1403 if (dcl->ops->dpy_text_cursor) {
bc2ed970 1404 dcl->ops->dpy_text_cursor(dcl, x, y);
7c20b4a3
GH
1405 }
1406 }
1407}
1408
c78f7137 1409void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
7c20b4a3 1410{
c78f7137 1411 DisplayState *s = con->ds;
7c20b4a3
GH
1412 struct DisplayChangeListener *dcl;
1413 QLIST_FOREACH(dcl, &s->listeners, next) {
1414 if (dcl->ops->dpy_text_update) {
bc2ed970 1415 dcl->ops->dpy_text_update(dcl, x, y, w, h);
7c20b4a3
GH
1416 }
1417 }
1418}
1419
c78f7137 1420void dpy_text_resize(QemuConsole *con, int w, int h)
7c20b4a3 1421{
c78f7137 1422 DisplayState *s = con->ds;
7c20b4a3
GH
1423 struct DisplayChangeListener *dcl;
1424 QLIST_FOREACH(dcl, &s->listeners, next) {
1425 if (dcl->ops->dpy_text_resize) {
bc2ed970 1426 dcl->ops->dpy_text_resize(dcl, w, h);
7c20b4a3
GH
1427 }
1428 }
1429}
1430
c78f7137 1431void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
7c20b4a3 1432{
c78f7137 1433 DisplayState *s = con->ds;
7c20b4a3
GH
1434 struct DisplayChangeListener *dcl;
1435 QLIST_FOREACH(dcl, &s->listeners, next) {
1436 if (dcl->ops->dpy_mouse_set) {
bc2ed970 1437 dcl->ops->dpy_mouse_set(dcl, x, y, on);
7c20b4a3
GH
1438 }
1439 }
1440}
1441
c78f7137 1442void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
7c20b4a3 1443{
c78f7137 1444 DisplayState *s = con->ds;
7c20b4a3
GH
1445 struct DisplayChangeListener *dcl;
1446 QLIST_FOREACH(dcl, &s->listeners, next) {
1447 if (dcl->ops->dpy_cursor_define) {
bc2ed970 1448 dcl->ops->dpy_cursor_define(dcl, cursor);
7c20b4a3
GH
1449 }
1450 }
1451}
1452
c78f7137 1453bool dpy_cursor_define_supported(QemuConsole *con)
7c20b4a3 1454{
c78f7137 1455 DisplayState *s = con->ds;
7c20b4a3
GH
1456 struct DisplayChangeListener *dcl;
1457 QLIST_FOREACH(dcl, &s->listeners, next) {
1458 if (dcl->ops->dpy_cursor_define) {
1459 return true;
1460 }
1461 }
1462 return false;
1463}
1464
98b50080
PB
1465static void dumb_display_init(void)
1466{
7267c094 1467 DisplayState *ds = g_malloc0(sizeof(DisplayState));
1802651c
JK
1468 int width = 640;
1469 int height = 480;
1470
1802651c
JK
1471 if (is_fixedsize_console()) {
1472 width = active_console->g_width;
1473 height = active_console->g_height;
1474 }
c78f7137 1475 ds->surface = qemu_create_displaysurface(width, height);
da229ef3 1476
98b50080
PB
1477 register_displaystate(ds);
1478}
1479
1480/***********************************************************/
1481/* register display */
1482
1483void register_displaystate(DisplayState *ds)
1484{
1485 DisplayState **s;
1486 s = &display_state;
1487 while (*s != NULL)
1488 s = &(*s)->next;
1489 ds->next = NULL;
1490 *s = ds;
1491}
1492
1493DisplayState *get_displaystate(void)
1494{
1495 if (!display_state) {
1496 dumb_display_init ();
1497 }
1498 return display_state;
1499}
1500
c78f7137
GH
1501QemuConsole *graphic_console_init(vga_hw_update_ptr update,
1502 vga_hw_invalidate_ptr invalidate,
1503 vga_hw_screen_dump_ptr screen_dump,
1504 vga_hw_text_update_ptr text_update,
1505 void *opaque)
95219897 1506{
76ffb0b4 1507 QemuConsole *s;
3023f332 1508 DisplayState *ds;
f0f2f976 1509
7267c094 1510 ds = (DisplayState *) g_malloc0(sizeof(DisplayState));
af3a9031 1511 s = new_console(ds, GRAPHIC_CONSOLE);
95219897
PB
1512 s->hw_update = update;
1513 s->hw_invalidate = invalidate;
1514 s->hw_screen_dump = screen_dump;
4d3b6f6e 1515 s->hw_text_update = text_update;
95219897 1516 s->hw = opaque;
3023f332 1517
c78f7137 1518 ds->surface = qemu_create_displaysurface(640, 480);
da229ef3 1519
f0f2f976 1520 register_displaystate(ds);
c78f7137 1521 return s;
e7f0ad58
FB
1522}
1523
95219897 1524int is_graphic_console(void)
e7f0ad58 1525{
4d3b6f6e 1526 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
e7f0ad58
FB
1527}
1528
c21bbcfa
AZ
1529int is_fixedsize_console(void)
1530{
1531 return active_console && active_console->console_type != TEXT_CONSOLE;
1532}
1533
4104833f
PB
1534static void text_console_set_echo(CharDriverState *chr, bool echo)
1535{
76ffb0b4 1536 QemuConsole *s = chr->opaque;
4104833f
PB
1537
1538 s->echo = echo;
1539}
1540
bf1bed81
JK
1541static void text_console_update_cursor(void *opaque)
1542{
76ffb0b4 1543 QemuConsole *s = opaque;
bf1bed81
JK
1544
1545 s->cursor_visible_phase = !s->cursor_visible_phase;
1546 vga_hw_invalidate();
1547 qemu_mod_timer(s->cursor_timer,
1548 qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1549}
1550
44b37b93 1551static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
e7f0ad58 1552{
76ffb0b4 1553 QemuConsole *s;
6d6f7c28 1554
491e114a 1555 s = chr->opaque;
6ea314d9 1556
e7f0ad58 1557 chr->chr_write = console_puts;
6fcfafb7 1558
e15d7371
FB
1559 s->out_fifo.buf = s->out_fifo_buf;
1560 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
7bd427d8 1561 s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
3023f332 1562 s->ds = ds;
3b46e624 1563
e7f0ad58
FB
1564 s->y_displayed = 0;
1565 s->y_base = 0;
1566 s->total_height = DEFAULT_BACKSCROLL;
1567 s->x = 0;
1568 s->y = 0;
491e114a 1569 if (s->console_type == TEXT_CONSOLE) {
1562e531
GH
1570 s->g_width = surface_width(s->ds->surface);
1571 s->g_height = surface_height(s->ds->surface);
491e114a 1572 }
6d6f7c28 1573
bf1bed81
JK
1574 s->cursor_timer =
1575 qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
1576
4d3b6f6e
AZ
1577 s->hw_invalidate = text_console_invalidate;
1578 s->hw_text_update = text_console_update;
1579 s->hw = s;
1580
6d6f7c28
PB
1581 /* Set text attribute defaults */
1582 s->t_attrib_default.bold = 0;
1583 s->t_attrib_default.uline = 0;
1584 s->t_attrib_default.blink = 0;
1585 s->t_attrib_default.invers = 0;
1586 s->t_attrib_default.unvisible = 0;
1587 s->t_attrib_default.fgcol = COLOR_WHITE;
1588 s->t_attrib_default.bgcol = COLOR_BLACK;
6d6f7c28
PB
1589 /* set current text attributes to default */
1590 s->t_attrib = s->t_attrib_default;
e7f0ad58
FB
1591 text_console_resize(s);
1592
51bfa4d3
GH
1593 if (chr->label) {
1594 char msg[128];
1595 int len;
1596
735ba588 1597 s->t_attrib.bgcol = COLOR_BLUE;
51bfa4d3
GH
1598 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1599 console_puts(chr, (uint8_t*)msg, len);
735ba588 1600 s->t_attrib = s->t_attrib_default;
51bfa4d3
GH
1601 }
1602
fee204fd 1603 qemu_chr_be_generic_open(chr);
ceecf1d1
AJ
1604 if (chr->init)
1605 chr->init(chr);
e7f0ad58 1606}
c60e08d9 1607
702ec69c 1608static CharDriverState *text_console_init(ChardevVC *vc)
2796dae0
AL
1609{
1610 CharDriverState *chr;
76ffb0b4 1611 QemuConsole *s;
702ec69c
GH
1612 unsigned width = 0;
1613 unsigned height = 0;
2796dae0 1614
7267c094 1615 chr = g_malloc0(sizeof(CharDriverState));
2796dae0 1616
702ec69c
GH
1617 if (vc->has_width) {
1618 width = vc->width;
1619 } else if (vc->has_cols) {
1620 width = vc->cols * FONT_WIDTH;
1621 }
491e114a 1622
702ec69c
GH
1623 if (vc->has_height) {
1624 height = vc->height;
1625 } else if (vc->has_rows) {
1626 height = vc->rows * FONT_HEIGHT;
1627 }
491e114a
PB
1628
1629 if (width == 0 || height == 0) {
1630 s = new_console(NULL, TEXT_CONSOLE);
1631 } else {
1632 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1633 }
1634
1635 if (!s) {
5354d083 1636 g_free(chr);
1f51470d 1637 return NULL;
491e114a
PB
1638 }
1639
1640 s->chr = chr;
1641 s->g_width = width;
1642 s->g_height = height;
1643 chr->opaque = s;
4104833f 1644 chr->chr_set_echo = text_console_set_echo;
1f51470d 1645 return chr;
2796dae0
AL
1646}
1647
d82831db
AL
1648static VcHandler *vc_handler = text_console_init;
1649
702ec69c 1650CharDriverState *vc_init(ChardevVC *vc)
d82831db 1651{
702ec69c 1652 return vc_handler(vc);
d82831db
AL
1653}
1654
1655void register_vc_handler(VcHandler *handler)
1656{
1657 vc_handler = handler;
1658}
1659
2796dae0
AL
1660void text_consoles_set_display(DisplayState *ds)
1661{
1662 int i;
1663
8811e1e1
MA
1664 for (i = 0; i < nb_consoles; i++) {
1665 if (consoles[i]->console_type != GRAPHIC_CONSOLE) {
1666 text_console_do_init(consoles[i]->chr, ds);
1667 }
2796dae0 1668 }
2796dae0
AL
1669}
1670
c78f7137 1671void qemu_console_resize(QemuConsole *s, int width, int height)
c60e08d9 1672{
3023f332
AL
1673 s->g_width = width;
1674 s->g_height = height;
1675 if (is_graphic_console()) {
da229ef3
GH
1676 DisplaySurface *surface;
1677 surface = qemu_create_displaysurface(width, height);
c78f7137 1678 dpy_gfx_replace_surface(s, surface);
c60e08d9
PB
1679 }
1680}
38334f76 1681
c78f7137 1682void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
3023f332 1683 int dst_x, int dst_y, int w, int h)
c21bbcfa 1684{
3023f332 1685 if (is_graphic_console()) {
c78f7137 1686 dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
38334f76
AZ
1687 }
1688}
7d957bd8 1689
c78f7137
GH
1690DisplaySurface *qemu_console_surface(QemuConsole *console)
1691{
1692 return console->ds->surface;
1693}
1694
1695DisplayState *qemu_console_displaystate(QemuConsole *console)
1696{
1697 return console->ds;
1698}
1699
0da2ea1b 1700PixelFormat qemu_different_endianness_pixelformat(int bpp)
7d957bd8
AL
1701{
1702 PixelFormat pf;
1703
1704 memset(&pf, 0x00, sizeof(PixelFormat));
1705
1706 pf.bits_per_pixel = bpp;
feadf1a4 1707 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
7d957bd8
AL
1708 pf.depth = bpp == 32 ? 24 : bpp;
1709
1710 switch (bpp) {
0da2ea1b 1711 case 24:
1712 pf.rmask = 0x000000FF;
1713 pf.gmask = 0x0000FF00;
1714 pf.bmask = 0x00FF0000;
1715 pf.rmax = 255;
1716 pf.gmax = 255;
1717 pf.bmax = 255;
1718 pf.rshift = 0;
1719 pf.gshift = 8;
1720 pf.bshift = 16;
90a1e3c0
AL
1721 pf.rbits = 8;
1722 pf.gbits = 8;
1723 pf.bbits = 8;
7d957bd8 1724 break;
0da2ea1b 1725 case 32:
1726 pf.rmask = 0x0000FF00;
1727 pf.gmask = 0x00FF0000;
1728 pf.bmask = 0xFF000000;
1729 pf.amask = 0x00000000;
1730 pf.amax = 255;
1731 pf.rmax = 255;
1732 pf.gmax = 255;
1733 pf.bmax = 255;
1734 pf.ashift = 0;
1735 pf.rshift = 8;
1736 pf.gshift = 16;
1737 pf.bshift = 24;
90a1e3c0
AL
1738 pf.rbits = 8;
1739 pf.gbits = 8;
1740 pf.bbits = 8;
1741 pf.abits = 8;
0da2ea1b 1742 break;
1743 default:
1744 break;
1745 }
1746 return pf;
1747}
1748
1749PixelFormat qemu_default_pixelformat(int bpp)
1750{
1751 PixelFormat pf;
1752
1753 memset(&pf, 0x00, sizeof(PixelFormat));
1754
1755 pf.bits_per_pixel = bpp;
feadf1a4 1756 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
0da2ea1b 1757 pf.depth = bpp == 32 ? 24 : bpp;
1758
1759 switch (bpp) {
b6278084
GH
1760 case 15:
1761 pf.bits_per_pixel = 16;
b6278084
GH
1762 pf.rmask = 0x00007c00;
1763 pf.gmask = 0x000003E0;
1764 pf.bmask = 0x0000001F;
1765 pf.rmax = 31;
1766 pf.gmax = 31;
1767 pf.bmax = 31;
1768 pf.rshift = 10;
1769 pf.gshift = 5;
1770 pf.bshift = 0;
1771 pf.rbits = 5;
1772 pf.gbits = 5;
1773 pf.bbits = 5;
1774 break;
7d957bd8
AL
1775 case 16:
1776 pf.rmask = 0x0000F800;
1777 pf.gmask = 0x000007E0;
1778 pf.bmask = 0x0000001F;
1779 pf.rmax = 31;
1780 pf.gmax = 63;
1781 pf.bmax = 31;
1782 pf.rshift = 11;
1783 pf.gshift = 5;
1784 pf.bshift = 0;
90a1e3c0
AL
1785 pf.rbits = 5;
1786 pf.gbits = 6;
1787 pf.bbits = 5;
7d957bd8
AL
1788 break;
1789 case 24:
0da2ea1b 1790 pf.rmask = 0x00FF0000;
1791 pf.gmask = 0x0000FF00;
1792 pf.bmask = 0x000000FF;
1793 pf.rmax = 255;
1794 pf.gmax = 255;
1795 pf.bmax = 255;
1796 pf.rshift = 16;
1797 pf.gshift = 8;
1798 pf.bshift = 0;
90a1e3c0
AL
1799 pf.rbits = 8;
1800 pf.gbits = 8;
1801 pf.bbits = 8;
0eba62e0 1802 break;
7d957bd8
AL
1803 case 32:
1804 pf.rmask = 0x00FF0000;
1805 pf.gmask = 0x0000FF00;
1806 pf.bmask = 0x000000FF;
1807 pf.rmax = 255;
1808 pf.gmax = 255;
1809 pf.bmax = 255;
1810 pf.rshift = 16;
1811 pf.gshift = 8;
1812 pf.bshift = 0;
90a1e3c0
AL
1813 pf.rbits = 8;
1814 pf.gbits = 8;
1815 pf.bbits = 8;
7d957bd8
AL
1816 break;
1817 default:
1818 break;
1819 }
1820 return pf;
1821}
01f45d98 1822
702ec69c
GH
1823static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
1824 Error **errp)
1825{
1826 int val;
1827
1828 backend->vc = g_new0(ChardevVC, 1);
1829
1830 val = qemu_opt_get_number(opts, "width", 0);
1831 if (val != 0) {
1832 backend->vc->has_width = true;
1833 backend->vc->width = val;
1834 }
1835
1836 val = qemu_opt_get_number(opts, "height", 0);
1837 if (val != 0) {
1838 backend->vc->has_height = true;
1839 backend->vc->height = val;
1840 }
1841
1842 val = qemu_opt_get_number(opts, "cols", 0);
1843 if (val != 0) {
1844 backend->vc->has_cols = true;
1845 backend->vc->cols = val;
1846 }
1847
1848 val = qemu_opt_get_number(opts, "rows", 0);
1849 if (val != 0) {
1850 backend->vc->has_rows = true;
1851 backend->vc->rows = val;
1852 }
1853}
1854
01f45d98
AL
1855static void register_types(void)
1856{
702ec69c
GH
1857 register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
1858 qemu_chr_parse_vc);
01f45d98
AL
1859}
1860
1861type_init(register_types);