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