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