Add support for dynamic ticks, by Luca Tettamanti and Dan Kenigsberg.
[qemu/dscho.git] / console.c
blobf4b8dc58ed773f0f4555d9e493c9351c5a2f80a8
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
24 #include "vl.h"
26 //#define DEBUG_CONSOLE
27 #define DEFAULT_BACKSCROLL 512
28 #define MAX_CONSOLES 12
30 #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
31 #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
33 typedef struct TextAttributes {
34 uint8_t fgcol:4;
35 uint8_t bgcol:4;
36 uint8_t bold:1;
37 uint8_t uline:1;
38 uint8_t blink:1;
39 uint8_t invers:1;
40 uint8_t unvisible:1;
41 } TextAttributes;
43 typedef struct TextCell {
44 uint8_t ch;
45 TextAttributes t_attrib;
46 } TextCell;
48 #define MAX_ESC_PARAMS 3
50 enum TTYState {
51 TTY_STATE_NORM,
52 TTY_STATE_ESC,
53 TTY_STATE_CSI,
56 typedef struct QEMUFIFO {
57 uint8_t *buf;
58 int buf_size;
59 int count, wptr, rptr;
60 } QEMUFIFO;
62 int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
64 int l, len;
66 l = f->buf_size - f->count;
67 if (len1 > l)
68 len1 = l;
69 len = len1;
70 while (len > 0) {
71 l = f->buf_size - f->wptr;
72 if (l > len)
73 l = len;
74 memcpy(f->buf + f->wptr, buf, l);
75 f->wptr += l;
76 if (f->wptr >= f->buf_size)
77 f->wptr = 0;
78 buf += l;
79 len -= l;
81 f->count += len1;
82 return len1;
85 int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
87 int l, len;
89 if (len1 > f->count)
90 len1 = f->count;
91 len = len1;
92 while (len > 0) {
93 l = f->buf_size - f->rptr;
94 if (l > len)
95 l = len;
96 memcpy(buf, f->buf + f->rptr, l);
97 f->rptr += l;
98 if (f->rptr >= f->buf_size)
99 f->rptr = 0;
100 buf += l;
101 len -= l;
103 f->count -= len1;
104 return len1;
107 typedef enum {
108 GRAPHIC_CONSOLE,
109 TEXT_CONSOLE,
110 TEXT_CONSOLE_FIXED_SIZE
111 } console_type_t;
113 /* ??? This is mis-named.
114 It is used for both text and graphical consoles. */
115 struct TextConsole {
116 console_type_t console_type;
117 DisplayState *ds;
118 /* Graphic console state. */
119 vga_hw_update_ptr hw_update;
120 vga_hw_invalidate_ptr hw_invalidate;
121 vga_hw_screen_dump_ptr hw_screen_dump;
122 void *hw;
124 int g_width, g_height;
125 int width;
126 int height;
127 int total_height;
128 int backscroll_height;
129 int x, y;
130 int x_saved, y_saved;
131 int y_displayed;
132 int y_base;
133 TextAttributes t_attrib_default; /* default text attributes */
134 TextAttributes t_attrib; /* currently active text attributes */
135 TextCell *cells;
137 enum TTYState state;
138 int esc_params[MAX_ESC_PARAMS];
139 int nb_esc_params;
141 CharDriverState *chr;
142 /* fifo for key pressed */
143 QEMUFIFO out_fifo;
144 uint8_t out_fifo_buf[16];
145 QEMUTimer *kbd_timer;
148 static TextConsole *active_console;
149 static TextConsole *consoles[MAX_CONSOLES];
150 static int nb_consoles = 0;
152 void vga_hw_update(void)
154 if (active_console && active_console->hw_update)
155 active_console->hw_update(active_console->hw);
158 void vga_hw_invalidate(void)
160 if (active_console->hw_invalidate)
161 active_console->hw_invalidate(active_console->hw);
164 void vga_hw_screen_dump(const char *filename)
166 /* There is currently no was of specifying which screen we want to dump,
167 so always dump the dirst one. */
168 if (consoles[0]->hw_screen_dump)
169 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
172 /* convert a RGBA color to a color index usable in graphic primitives */
173 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
175 unsigned int r, g, b, color;
177 switch(ds->depth) {
178 #if 0
179 case 8:
180 r = (rgba >> 16) & 0xff;
181 g = (rgba >> 8) & 0xff;
182 b = (rgba) & 0xff;
183 color = (rgb_to_index[r] * 6 * 6) +
184 (rgb_to_index[g] * 6) +
185 (rgb_to_index[b]);
186 break;
187 #endif
188 case 15:
189 r = (rgba >> 16) & 0xff;
190 g = (rgba >> 8) & 0xff;
191 b = (rgba) & 0xff;
192 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
193 break;
194 case 16:
195 r = (rgba >> 16) & 0xff;
196 g = (rgba >> 8) & 0xff;
197 b = (rgba) & 0xff;
198 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
199 break;
200 case 32:
201 default:
202 color = rgba;
203 break;
205 return color;
208 static void vga_fill_rect (DisplayState *ds,
209 int posx, int posy, int width, int height, uint32_t color)
211 uint8_t *d, *d1;
212 int x, y, bpp;
214 bpp = (ds->depth + 7) >> 3;
215 d1 = ds->data +
216 ds->linesize * posy + bpp * posx;
217 for (y = 0; y < height; y++) {
218 d = d1;
219 switch(bpp) {
220 case 1:
221 for (x = 0; x < width; x++) {
222 *((uint8_t *)d) = color;
223 d++;
225 break;
226 case 2:
227 for (x = 0; x < width; x++) {
228 *((uint16_t *)d) = color;
229 d += 2;
231 break;
232 case 4:
233 for (x = 0; x < width; x++) {
234 *((uint32_t *)d) = color;
235 d += 4;
237 break;
239 d1 += ds->linesize;
243 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
244 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
246 const uint8_t *s;
247 uint8_t *d;
248 int wb, y, bpp;
250 bpp = (ds->depth + 7) >> 3;
251 wb = w * bpp;
252 if (yd <= ys) {
253 s = ds->data +
254 ds->linesize * ys + bpp * xs;
255 d = ds->data +
256 ds->linesize * yd + bpp * xd;
257 for (y = 0; y < h; y++) {
258 memmove(d, s, wb);
259 d += ds->linesize;
260 s += ds->linesize;
262 } else {
263 s = ds->data +
264 ds->linesize * (ys + h - 1) + bpp * xs;
265 d = ds->data +
266 ds->linesize * (yd + h - 1) + bpp * xd;
267 for (y = 0; y < h; y++) {
268 memmove(d, s, wb);
269 d -= ds->linesize;
270 s -= ds->linesize;
275 /***********************************************************/
276 /* basic char display */
278 #define FONT_HEIGHT 16
279 #define FONT_WIDTH 8
281 #include "vgafont.h"
283 #define cbswap_32(__x) \
284 ((uint32_t)( \
285 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
286 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
287 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
288 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
290 #ifdef WORDS_BIGENDIAN
291 #define PAT(x) x
292 #else
293 #define PAT(x) cbswap_32(x)
294 #endif
296 static const uint32_t dmask16[16] = {
297 PAT(0x00000000),
298 PAT(0x000000ff),
299 PAT(0x0000ff00),
300 PAT(0x0000ffff),
301 PAT(0x00ff0000),
302 PAT(0x00ff00ff),
303 PAT(0x00ffff00),
304 PAT(0x00ffffff),
305 PAT(0xff000000),
306 PAT(0xff0000ff),
307 PAT(0xff00ff00),
308 PAT(0xff00ffff),
309 PAT(0xffff0000),
310 PAT(0xffff00ff),
311 PAT(0xffffff00),
312 PAT(0xffffffff),
315 static const uint32_t dmask4[4] = {
316 PAT(0x00000000),
317 PAT(0x0000ffff),
318 PAT(0xffff0000),
319 PAT(0xffffffff),
322 static uint32_t color_table[2][8];
324 enum color_names {
325 COLOR_BLACK = 0,
326 COLOR_RED = 1,
327 COLOR_GREEN = 2,
328 COLOR_YELLOW = 3,
329 COLOR_BLUE = 4,
330 COLOR_MAGENTA = 5,
331 COLOR_CYAN = 6,
332 COLOR_WHITE = 7
335 static const uint32_t color_table_rgb[2][8] = {
336 { /* dark */
337 QEMU_RGB(0x00, 0x00, 0x00), /* black */
338 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
339 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
340 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
341 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
342 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
343 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
344 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
346 { /* bright */
347 QEMU_RGB(0x00, 0x00, 0x00), /* black */
348 QEMU_RGB(0xff, 0x00, 0x00), /* red */
349 QEMU_RGB(0x00, 0xff, 0x00), /* green */
350 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
351 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
352 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
353 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
354 QEMU_RGB(0xff, 0xff, 0xff), /* white */
358 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
360 switch(ds->depth) {
361 case 8:
362 col |= col << 8;
363 col |= col << 16;
364 break;
365 case 15:
366 case 16:
367 col |= col << 16;
368 break;
369 default:
370 break;
373 return col;
375 #ifdef DEBUG_CONSOLE
376 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
378 if (t_attrib->bold) {
379 printf("b");
380 } else {
381 printf(" ");
383 if (t_attrib->uline) {
384 printf("u");
385 } else {
386 printf(" ");
388 if (t_attrib->blink) {
389 printf("l");
390 } else {
391 printf(" ");
393 if (t_attrib->invers) {
394 printf("i");
395 } else {
396 printf(" ");
398 if (t_attrib->unvisible) {
399 printf("n");
400 } else {
401 printf(" ");
404 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
406 #endif
408 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
409 TextAttributes *t_attrib)
411 uint8_t *d;
412 const uint8_t *font_ptr;
413 unsigned int font_data, linesize, xorcol, bpp;
414 int i;
415 unsigned int fgcol, bgcol;
417 #ifdef DEBUG_CONSOLE
418 printf("x: %2i y: %2i", x, y);
419 console_print_text_attributes(t_attrib, ch);
420 #endif
422 if (t_attrib->invers) {
423 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
424 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
425 } else {
426 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
427 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
430 bpp = (ds->depth + 7) >> 3;
431 d = ds->data +
432 ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
433 linesize = ds->linesize;
434 font_ptr = vgafont16 + FONT_HEIGHT * ch;
435 xorcol = bgcol ^ fgcol;
436 switch(ds->depth) {
437 case 8:
438 for(i = 0; i < FONT_HEIGHT; i++) {
439 font_data = *font_ptr++;
440 if (t_attrib->uline
441 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
442 font_data = 0xFFFF;
444 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
445 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
446 d += linesize;
448 break;
449 case 16:
450 case 15:
451 for(i = 0; i < FONT_HEIGHT; i++) {
452 font_data = *font_ptr++;
453 if (t_attrib->uline
454 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
455 font_data = 0xFFFF;
457 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
458 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
459 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
460 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
461 d += linesize;
463 break;
464 case 32:
465 for(i = 0; i < FONT_HEIGHT; i++) {
466 font_data = *font_ptr++;
467 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
468 font_data = 0xFFFF;
470 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
471 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
472 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
473 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
474 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
475 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
476 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
477 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
478 d += linesize;
480 break;
484 static void text_console_resize(TextConsole *s)
486 TextCell *cells, *c, *c1;
487 int w1, x, y, last_width;
489 last_width = s->width;
490 s->width = s->g_width / FONT_WIDTH;
491 s->height = s->g_height / FONT_HEIGHT;
493 w1 = last_width;
494 if (s->width < w1)
495 w1 = s->width;
497 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
498 for(y = 0; y < s->total_height; y++) {
499 c = &cells[y * s->width];
500 if (w1 > 0) {
501 c1 = &s->cells[y * last_width];
502 for(x = 0; x < w1; x++) {
503 *c++ = *c1++;
506 for(x = w1; x < s->width; x++) {
507 c->ch = ' ';
508 c->t_attrib = s->t_attrib_default;
509 c++;
512 free(s->cells);
513 s->cells = cells;
516 static void update_xy(TextConsole *s, int x, int y)
518 TextCell *c;
519 int y1, y2;
521 if (s == active_console) {
522 y1 = (s->y_base + y) % s->total_height;
523 y2 = y1 - s->y_displayed;
524 if (y2 < 0)
525 y2 += s->total_height;
526 if (y2 < s->height) {
527 c = &s->cells[y1 * s->width + x];
528 vga_putcharxy(s->ds, x, y2, c->ch,
529 &(c->t_attrib));
530 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
531 FONT_WIDTH, FONT_HEIGHT);
536 static void console_show_cursor(TextConsole *s, int show)
538 TextCell *c;
539 int y, y1;
541 if (s == active_console) {
542 int x = s->x;
543 if (x >= s->width) {
544 x = s->width - 1;
546 y1 = (s->y_base + s->y) % s->total_height;
547 y = y1 - s->y_displayed;
548 if (y < 0)
549 y += s->total_height;
550 if (y < s->height) {
551 c = &s->cells[y1 * s->width + x];
552 if (show) {
553 TextAttributes t_attrib = s->t_attrib_default;
554 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
555 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
556 } else {
557 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
559 dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
560 FONT_WIDTH, FONT_HEIGHT);
565 static void console_refresh(TextConsole *s)
567 TextCell *c;
568 int x, y, y1;
570 if (s != active_console)
571 return;
573 vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
574 color_table[0][COLOR_BLACK]);
575 y1 = s->y_displayed;
576 for(y = 0; y < s->height; y++) {
577 c = s->cells + y1 * s->width;
578 for(x = 0; x < s->width; x++) {
579 vga_putcharxy(s->ds, x, y, c->ch,
580 &(c->t_attrib));
581 c++;
583 if (++y1 == s->total_height)
584 y1 = 0;
586 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
587 console_show_cursor(s, 1);
590 static void console_scroll(int ydelta)
592 TextConsole *s;
593 int i, y1;
595 s = active_console;
596 if (!s || (s->console_type == GRAPHIC_CONSOLE))
597 return;
599 if (ydelta > 0) {
600 for(i = 0; i < ydelta; i++) {
601 if (s->y_displayed == s->y_base)
602 break;
603 if (++s->y_displayed == s->total_height)
604 s->y_displayed = 0;
606 } else {
607 ydelta = -ydelta;
608 i = s->backscroll_height;
609 if (i > s->total_height - s->height)
610 i = s->total_height - s->height;
611 y1 = s->y_base - i;
612 if (y1 < 0)
613 y1 += s->total_height;
614 for(i = 0; i < ydelta; i++) {
615 if (s->y_displayed == y1)
616 break;
617 if (--s->y_displayed < 0)
618 s->y_displayed = s->total_height - 1;
621 console_refresh(s);
624 static void console_put_lf(TextConsole *s)
626 TextCell *c;
627 int x, y1;
629 s->y++;
630 if (s->y >= s->height) {
631 s->y = s->height - 1;
633 if (s->y_displayed == s->y_base) {
634 if (++s->y_displayed == s->total_height)
635 s->y_displayed = 0;
637 if (++s->y_base == s->total_height)
638 s->y_base = 0;
639 if (s->backscroll_height < s->total_height)
640 s->backscroll_height++;
641 y1 = (s->y_base + s->height - 1) % s->total_height;
642 c = &s->cells[y1 * s->width];
643 for(x = 0; x < s->width; x++) {
644 c->ch = ' ';
645 c->t_attrib = s->t_attrib_default;
646 c++;
648 if (s == active_console && s->y_displayed == s->y_base) {
649 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
650 s->width * FONT_WIDTH,
651 (s->height - 1) * FONT_HEIGHT);
652 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
653 s->width * FONT_WIDTH, FONT_HEIGHT,
654 color_table[0][s->t_attrib_default.bgcol]);
655 dpy_update(s->ds, 0, 0,
656 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
661 /* Set console attributes depending on the current escape codes.
662 * NOTE: I know this code is not very efficient (checking every color for it
663 * self) but it is more readable and better maintainable.
665 static void console_handle_escape(TextConsole *s)
667 int i;
669 for (i=0; i<s->nb_esc_params; i++) {
670 switch (s->esc_params[i]) {
671 case 0: /* reset all console attributes to default */
672 s->t_attrib = s->t_attrib_default;
673 break;
674 case 1:
675 s->t_attrib.bold = 1;
676 break;
677 case 4:
678 s->t_attrib.uline = 1;
679 break;
680 case 5:
681 s->t_attrib.blink = 1;
682 break;
683 case 7:
684 s->t_attrib.invers = 1;
685 break;
686 case 8:
687 s->t_attrib.unvisible = 1;
688 break;
689 case 22:
690 s->t_attrib.bold = 0;
691 break;
692 case 24:
693 s->t_attrib.uline = 0;
694 break;
695 case 25:
696 s->t_attrib.blink = 0;
697 break;
698 case 27:
699 s->t_attrib.invers = 0;
700 break;
701 case 28:
702 s->t_attrib.unvisible = 0;
703 break;
704 /* set foreground color */
705 case 30:
706 s->t_attrib.fgcol=COLOR_BLACK;
707 break;
708 case 31:
709 s->t_attrib.fgcol=COLOR_RED;
710 break;
711 case 32:
712 s->t_attrib.fgcol=COLOR_GREEN;
713 break;
714 case 33:
715 s->t_attrib.fgcol=COLOR_YELLOW;
716 break;
717 case 34:
718 s->t_attrib.fgcol=COLOR_BLUE;
719 break;
720 case 35:
721 s->t_attrib.fgcol=COLOR_MAGENTA;
722 break;
723 case 36:
724 s->t_attrib.fgcol=COLOR_CYAN;
725 break;
726 case 37:
727 s->t_attrib.fgcol=COLOR_WHITE;
728 break;
729 /* set background color */
730 case 40:
731 s->t_attrib.bgcol=COLOR_BLACK;
732 break;
733 case 41:
734 s->t_attrib.bgcol=COLOR_RED;
735 break;
736 case 42:
737 s->t_attrib.bgcol=COLOR_GREEN;
738 break;
739 case 43:
740 s->t_attrib.bgcol=COLOR_YELLOW;
741 break;
742 case 44:
743 s->t_attrib.bgcol=COLOR_BLUE;
744 break;
745 case 45:
746 s->t_attrib.bgcol=COLOR_MAGENTA;
747 break;
748 case 46:
749 s->t_attrib.bgcol=COLOR_CYAN;
750 break;
751 case 47:
752 s->t_attrib.bgcol=COLOR_WHITE;
753 break;
758 static void console_clear_xy(TextConsole *s, int x, int y)
760 int y1 = (s->y_base + y) % s->total_height;
761 TextCell *c = &s->cells[y1 * s->width + x];
762 c->ch = ' ';
763 c->t_attrib = s->t_attrib_default;
764 c++;
765 update_xy(s, x, y);
768 static void console_putchar(TextConsole *s, int ch)
770 TextCell *c;
771 int y1, i;
772 int x, y;
774 switch(s->state) {
775 case TTY_STATE_NORM:
776 switch(ch) {
777 case '\r': /* carriage return */
778 s->x = 0;
779 break;
780 case '\n': /* newline */
781 console_put_lf(s);
782 break;
783 case '\b': /* backspace */
784 if (s->x > 0)
785 s->x--;
786 break;
787 case '\t': /* tabspace */
788 if (s->x + (8 - (s->x % 8)) > s->width) {
789 s->x = 0;
790 console_put_lf(s);
791 } else {
792 s->x = s->x + (8 - (s->x % 8));
794 break;
795 case '\a': /* alert aka. bell */
796 /* TODO: has to be implemented */
797 break;
798 case 14:
799 /* SI (shift in), character set 0 (ignored) */
800 break;
801 case 15:
802 /* SO (shift out), character set 1 (ignored) */
803 break;
804 case 27: /* esc (introducing an escape sequence) */
805 s->state = TTY_STATE_ESC;
806 break;
807 default:
808 if (s->x >= s->width) {
809 /* line wrap */
810 s->x = 0;
811 console_put_lf(s);
813 y1 = (s->y_base + s->y) % s->total_height;
814 c = &s->cells[y1 * s->width + s->x];
815 c->ch = ch;
816 c->t_attrib = s->t_attrib;
817 update_xy(s, s->x, s->y);
818 s->x++;
819 break;
821 break;
822 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
823 if (ch == '[') {
824 for(i=0;i<MAX_ESC_PARAMS;i++)
825 s->esc_params[i] = 0;
826 s->nb_esc_params = 0;
827 s->state = TTY_STATE_CSI;
828 } else {
829 s->state = TTY_STATE_NORM;
831 break;
832 case TTY_STATE_CSI: /* handle escape sequence parameters */
833 if (ch >= '0' && ch <= '9') {
834 if (s->nb_esc_params < MAX_ESC_PARAMS) {
835 s->esc_params[s->nb_esc_params] =
836 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
838 } else {
839 s->nb_esc_params++;
840 if (ch == ';')
841 break;
842 #ifdef DEBUG_CONSOLE
843 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
844 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
845 #endif
846 s->state = TTY_STATE_NORM;
847 switch(ch) {
848 case 'A':
849 /* move cursor up */
850 if (s->esc_params[0] == 0) {
851 s->esc_params[0] = 1;
853 s->y -= s->esc_params[0];
854 if (s->y < 0) {
855 s->y = 0;
857 break;
858 case 'B':
859 /* move cursor down */
860 if (s->esc_params[0] == 0) {
861 s->esc_params[0] = 1;
863 s->y += s->esc_params[0];
864 if (s->y >= s->height) {
865 s->y = s->height - 1;
867 break;
868 case 'C':
869 /* move cursor right */
870 if (s->esc_params[0] == 0) {
871 s->esc_params[0] = 1;
873 s->x += s->esc_params[0];
874 if (s->x >= s->width) {
875 s->x = s->width - 1;
877 break;
878 case 'D':
879 /* move cursor left */
880 if (s->esc_params[0] == 0) {
881 s->esc_params[0] = 1;
883 s->x -= s->esc_params[0];
884 if (s->x < 0) {
885 s->x = 0;
887 break;
888 case 'G':
889 /* move cursor to column */
890 s->x = s->esc_params[0] - 1;
891 if (s->x < 0) {
892 s->x = 0;
894 break;
895 case 'f':
896 case 'H':
897 /* move cursor to row, column */
898 s->x = s->esc_params[1] - 1;
899 if (s->x < 0) {
900 s->x = 0;
902 s->y = s->esc_params[0] - 1;
903 if (s->y < 0) {
904 s->y = 0;
906 break;
907 case 'J':
908 switch (s->esc_params[0]) {
909 case 0:
910 /* clear to end of screen */
911 for (y = s->y; y < s->height; y++) {
912 for (x = 0; x < s->width; x++) {
913 if (y == s->y && x < s->x) {
914 continue;
916 console_clear_xy(s, x, y);
919 break;
920 case 1:
921 /* clear from beginning of screen */
922 for (y = 0; y <= s->y; y++) {
923 for (x = 0; x < s->width; x++) {
924 if (y == s->y && x > s->x) {
925 break;
927 console_clear_xy(s, x, y);
930 break;
931 case 2:
932 /* clear entire screen */
933 for (y = 0; y <= s->height; y++) {
934 for (x = 0; x < s->width; x++) {
935 console_clear_xy(s, x, y);
938 break;
940 case 'K':
941 switch (s->esc_params[0]) {
942 case 0:
943 /* clear to eol */
944 for(x = s->x; x < s->width; x++) {
945 console_clear_xy(s, x, s->y);
947 break;
948 case 1:
949 /* clear from beginning of line */
950 for (x = 0; x <= s->x; x++) {
951 console_clear_xy(s, x, s->y);
953 break;
954 case 2:
955 /* clear entire line */
956 for(x = 0; x < s->width; x++) {
957 console_clear_xy(s, x, s->y);
959 break;
961 break;
962 case 'm':
963 console_handle_escape(s);
964 break;
965 case 'n':
966 /* report cursor position */
967 /* TODO: send ESC[row;colR */
968 break;
969 case 's':
970 /* save cursor position */
971 s->x_saved = s->x;
972 s->y_saved = s->y;
973 break;
974 case 'u':
975 /* restore cursor position */
976 s->x = s->x_saved;
977 s->y = s->y_saved;
978 break;
979 default:
980 #ifdef DEBUG_CONSOLE
981 fprintf(stderr, "unhandled escape character '%c'\n", ch);
982 #endif
983 break;
985 break;
990 void console_select(unsigned int index)
992 TextConsole *s;
994 if (index >= MAX_CONSOLES)
995 return;
996 s = consoles[index];
997 if (s) {
998 active_console = s;
999 if (s->console_type != GRAPHIC_CONSOLE) {
1000 if (s->g_width != s->ds->width ||
1001 s->g_height != s->ds->height) {
1002 if (s->console_type == TEXT_CONSOLE_FIXED_SIZE) {
1003 dpy_resize(s->ds, s->g_width, s->g_height);
1004 } else {
1005 s->g_width = s->ds->width;
1006 s->g_height = s->ds->height;
1007 text_console_resize(s);
1010 console_refresh(s);
1011 } else {
1012 vga_hw_invalidate();
1017 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1019 TextConsole *s = chr->opaque;
1020 int i;
1022 console_show_cursor(s, 0);
1023 for(i = 0; i < len; i++) {
1024 console_putchar(s, buf[i]);
1026 console_show_cursor(s, 1);
1027 return len;
1030 static void console_send_event(CharDriverState *chr, int event)
1032 TextConsole *s = chr->opaque;
1033 int i;
1035 if (event == CHR_EVENT_FOCUS) {
1036 for(i = 0; i < nb_consoles; i++) {
1037 if (consoles[i] == s) {
1038 console_select(i);
1039 break;
1045 static void kbd_send_chars(void *opaque)
1047 TextConsole *s = opaque;
1048 int len;
1049 uint8_t buf[16];
1051 len = qemu_chr_can_read(s->chr);
1052 if (len > s->out_fifo.count)
1053 len = s->out_fifo.count;
1054 if (len > 0) {
1055 if (len > sizeof(buf))
1056 len = sizeof(buf);
1057 qemu_fifo_read(&s->out_fifo, buf, len);
1058 qemu_chr_read(s->chr, buf, len);
1060 /* characters are pending: we send them a bit later (XXX:
1061 horrible, should change char device API) */
1062 if (s->out_fifo.count > 0) {
1063 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1067 /* called when an ascii key is pressed */
1068 void kbd_put_keysym(int keysym)
1070 TextConsole *s;
1071 uint8_t buf[16], *q;
1072 int c;
1074 s = active_console;
1075 if (!s || (s->console_type == GRAPHIC_CONSOLE))
1076 return;
1078 switch(keysym) {
1079 case QEMU_KEY_CTRL_UP:
1080 console_scroll(-1);
1081 break;
1082 case QEMU_KEY_CTRL_DOWN:
1083 console_scroll(1);
1084 break;
1085 case QEMU_KEY_CTRL_PAGEUP:
1086 console_scroll(-10);
1087 break;
1088 case QEMU_KEY_CTRL_PAGEDOWN:
1089 console_scroll(10);
1090 break;
1091 default:
1092 /* convert the QEMU keysym to VT100 key string */
1093 q = buf;
1094 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1095 *q++ = '\033';
1096 *q++ = '[';
1097 c = keysym - 0xe100;
1098 if (c >= 10)
1099 *q++ = '0' + (c / 10);
1100 *q++ = '0' + (c % 10);
1101 *q++ = '~';
1102 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1103 *q++ = '\033';
1104 *q++ = '[';
1105 *q++ = keysym & 0xff;
1106 } else {
1107 *q++ = keysym;
1109 if (s->chr->chr_read) {
1110 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1111 kbd_send_chars(s);
1113 break;
1117 static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1119 TextConsole *s;
1120 int i;
1122 if (nb_consoles >= MAX_CONSOLES)
1123 return NULL;
1124 s = qemu_mallocz(sizeof(TextConsole));
1125 if (!s) {
1126 return NULL;
1128 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1129 (console_type == GRAPHIC_CONSOLE))) {
1130 active_console = s;
1132 s->ds = ds;
1133 s->console_type = console_type;
1134 if (console_type != GRAPHIC_CONSOLE) {
1135 consoles[nb_consoles++] = s;
1136 } else {
1137 /* HACK: Put graphical consoles before text consoles. */
1138 for (i = nb_consoles; i > 0; i--) {
1139 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1140 break;
1141 consoles[i] = consoles[i - 1];
1143 consoles[i] = s;
1145 return s;
1148 TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1149 vga_hw_invalidate_ptr invalidate,
1150 vga_hw_screen_dump_ptr screen_dump,
1151 void *opaque)
1153 TextConsole *s;
1155 s = new_console(ds, GRAPHIC_CONSOLE);
1156 if (!s)
1157 return NULL;
1158 s->hw_update = update;
1159 s->hw_invalidate = invalidate;
1160 s->hw_screen_dump = screen_dump;
1161 s->hw = opaque;
1162 return s;
1165 int is_graphic_console(void)
1167 return active_console->console_type == GRAPHIC_CONSOLE;
1170 CharDriverState *text_console_init(DisplayState *ds, const char *p)
1172 CharDriverState *chr;
1173 TextConsole *s;
1174 int i,j;
1175 unsigned width;
1176 unsigned height;
1177 static int color_inited;
1179 chr = qemu_mallocz(sizeof(CharDriverState));
1180 if (!chr)
1181 return NULL;
1182 s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
1183 if (!s) {
1184 free(chr);
1185 return NULL;
1187 chr->opaque = s;
1188 chr->chr_write = console_puts;
1189 chr->chr_send_event = console_send_event;
1191 s->chr = chr;
1192 s->out_fifo.buf = s->out_fifo_buf;
1193 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1194 s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1196 if (!color_inited) {
1197 color_inited = 1;
1198 for(j = 0; j < 2; j++) {
1199 for(i = 0; i < 8; i++) {
1200 color_table[j][i] = col_expand(s->ds,
1201 vga_get_color(s->ds, color_table_rgb[j][i]));
1205 s->y_displayed = 0;
1206 s->y_base = 0;
1207 s->total_height = DEFAULT_BACKSCROLL;
1208 s->x = 0;
1209 s->y = 0;
1210 width = s->ds->width;
1211 height = s->ds->height;
1212 if (p != 0) {
1213 width = strtoul(p, (char **)&p, 10);
1214 if (*p == 'C') {
1215 p++;
1216 width *= FONT_WIDTH;
1218 if (*p == 'x') {
1219 p++;
1220 height = strtoul(p, (char **)&p, 10);
1221 if (*p == 'C') {
1222 p++;
1223 height *= FONT_HEIGHT;
1227 s->g_width = width;
1228 s->g_height = height;
1230 /* Set text attribute defaults */
1231 s->t_attrib_default.bold = 0;
1232 s->t_attrib_default.uline = 0;
1233 s->t_attrib_default.blink = 0;
1234 s->t_attrib_default.invers = 0;
1235 s->t_attrib_default.unvisible = 0;
1236 s->t_attrib_default.fgcol = COLOR_WHITE;
1237 s->t_attrib_default.bgcol = COLOR_BLACK;
1239 /* set current text attributes to default */
1240 s->t_attrib = s->t_attrib_default;
1241 text_console_resize(s);
1243 qemu_chr_reset(chr);
1245 return chr;