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