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