Merge qemu-0.9.0
[qemu-kvm/fedora.git] / console.c
blob2b3cd669da5f2487a20a1b87ce9c8a2b2b3b3f87
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 /* ??? This is mis-named.
108 It is used for both text and graphical consoles. */
109 struct TextConsole {
110 int text_console; /* true if text console */
111 DisplayState *ds;
112 /* Graphic console state. */
113 vga_hw_update_ptr hw_update;
114 vga_hw_invalidate_ptr hw_invalidate;
115 vga_hw_screen_dump_ptr hw_screen_dump;
116 void *hw;
118 int g_width, g_height;
119 int width;
120 int height;
121 int total_height;
122 int backscroll_height;
123 int x, y;
124 int x_saved, y_saved;
125 int y_displayed;
126 int y_base;
127 TextAttributes t_attrib_default; /* default text attributes */
128 TextAttributes t_attrib; /* currently active text attributes */
129 TextCell *cells;
131 enum TTYState state;
132 int esc_params[MAX_ESC_PARAMS];
133 int nb_esc_params;
135 CharDriverState *chr;
136 /* fifo for key pressed */
137 QEMUFIFO out_fifo;
138 uint8_t out_fifo_buf[16];
139 QEMUTimer *kbd_timer;
142 static TextConsole *active_console;
143 static TextConsole *consoles[MAX_CONSOLES];
144 static int nb_consoles = 0;
146 void vga_hw_update(void)
148 if (active_console && active_console->hw_update)
149 active_console->hw_update(active_console->hw);
152 void vga_hw_invalidate(void)
154 if (active_console->hw_invalidate)
155 active_console->hw_invalidate(active_console->hw);
158 void vga_hw_screen_dump(const char *filename)
160 /* There is currently no was of specifying which screen we want to dump,
161 so always dump the dirst one. */
162 if (consoles[0]->hw_screen_dump)
163 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
166 /* convert a RGBA color to a color index usable in graphic primitives */
167 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
169 unsigned int r, g, b, color;
171 switch(ds->depth) {
172 #if 0
173 case 8:
174 r = (rgba >> 16) & 0xff;
175 g = (rgba >> 8) & 0xff;
176 b = (rgba) & 0xff;
177 color = (rgb_to_index[r] * 6 * 6) +
178 (rgb_to_index[g] * 6) +
179 (rgb_to_index[b]);
180 break;
181 #endif
182 case 15:
183 r = (rgba >> 16) & 0xff;
184 g = (rgba >> 8) & 0xff;
185 b = (rgba) & 0xff;
186 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
187 break;
188 case 16:
189 r = (rgba >> 16) & 0xff;
190 g = (rgba >> 8) & 0xff;
191 b = (rgba) & 0xff;
192 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
193 break;
194 case 32:
195 default:
196 color = rgba;
197 break;
199 return color;
202 static void vga_fill_rect (DisplayState *ds,
203 int posx, int posy, int width, int height, uint32_t color)
205 uint8_t *d, *d1;
206 int x, y, bpp;
208 bpp = (ds->depth + 7) >> 3;
209 d1 = ds->data +
210 ds->linesize * posy + bpp * posx;
211 for (y = 0; y < height; y++) {
212 d = d1;
213 switch(bpp) {
214 case 1:
215 for (x = 0; x < width; x++) {
216 *((uint8_t *)d) = color;
217 d++;
219 break;
220 case 2:
221 for (x = 0; x < width; x++) {
222 *((uint16_t *)d) = color;
223 d += 2;
225 break;
226 case 4:
227 for (x = 0; x < width; x++) {
228 *((uint32_t *)d) = color;
229 d += 4;
231 break;
233 d1 += ds->linesize;
237 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
238 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
240 const uint8_t *s;
241 uint8_t *d;
242 int wb, y, bpp;
244 bpp = (ds->depth + 7) >> 3;
245 wb = w * bpp;
246 if (yd <= ys) {
247 s = ds->data +
248 ds->linesize * ys + bpp * xs;
249 d = ds->data +
250 ds->linesize * yd + bpp * xd;
251 for (y = 0; y < h; y++) {
252 memmove(d, s, wb);
253 d += ds->linesize;
254 s += ds->linesize;
256 } else {
257 s = ds->data +
258 ds->linesize * (ys + h - 1) + bpp * xs;
259 d = ds->data +
260 ds->linesize * (yd + h - 1) + bpp * xd;
261 for (y = 0; y < h; y++) {
262 memmove(d, s, wb);
263 d -= ds->linesize;
264 s -= ds->linesize;
269 /***********************************************************/
270 /* basic char display */
272 #define FONT_HEIGHT 16
273 #define FONT_WIDTH 8
275 #include "vgafont.h"
277 #define cbswap_32(__x) \
278 ((uint32_t)( \
279 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
280 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
281 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
282 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
284 #ifdef WORDS_BIGENDIAN
285 #define PAT(x) x
286 #else
287 #define PAT(x) cbswap_32(x)
288 #endif
290 static const uint32_t dmask16[16] = {
291 PAT(0x00000000),
292 PAT(0x000000ff),
293 PAT(0x0000ff00),
294 PAT(0x0000ffff),
295 PAT(0x00ff0000),
296 PAT(0x00ff00ff),
297 PAT(0x00ffff00),
298 PAT(0x00ffffff),
299 PAT(0xff000000),
300 PAT(0xff0000ff),
301 PAT(0xff00ff00),
302 PAT(0xff00ffff),
303 PAT(0xffff0000),
304 PAT(0xffff00ff),
305 PAT(0xffffff00),
306 PAT(0xffffffff),
309 static const uint32_t dmask4[4] = {
310 PAT(0x00000000),
311 PAT(0x0000ffff),
312 PAT(0xffff0000),
313 PAT(0xffffffff),
316 static uint32_t color_table[2][8];
318 enum color_names {
319 COLOR_BLACK = 0,
320 COLOR_RED = 1,
321 COLOR_GREEN = 2,
322 COLOR_YELLOW = 3,
323 COLOR_BLUE = 4,
324 COLOR_MAGENTA = 5,
325 COLOR_CYAN = 6,
326 COLOR_WHITE = 7
329 static const uint32_t color_table_rgb[2][8] = {
330 { /* dark */
331 QEMU_RGB(0x00, 0x00, 0x00), /* black */
332 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
333 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
334 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
335 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
336 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
337 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
338 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
340 { /* bright */
341 QEMU_RGB(0x00, 0x00, 0x00), /* black */
342 QEMU_RGB(0xff, 0x00, 0x00), /* red */
343 QEMU_RGB(0x00, 0xff, 0x00), /* green */
344 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
345 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
346 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
347 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
348 QEMU_RGB(0xff, 0xff, 0xff), /* white */
352 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
354 switch(ds->depth) {
355 case 8:
356 col |= col << 8;
357 col |= col << 16;
358 break;
359 case 15:
360 case 16:
361 col |= col << 16;
362 break;
363 default:
364 break;
367 return col;
369 #ifdef DEBUG_CONSOLE
370 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
372 if (t_attrib->bold) {
373 printf("b");
374 } else {
375 printf(" ");
377 if (t_attrib->uline) {
378 printf("u");
379 } else {
380 printf(" ");
382 if (t_attrib->blink) {
383 printf("l");
384 } else {
385 printf(" ");
387 if (t_attrib->invers) {
388 printf("i");
389 } else {
390 printf(" ");
392 if (t_attrib->unvisible) {
393 printf("n");
394 } else {
395 printf(" ");
398 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
400 #endif
402 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
403 TextAttributes *t_attrib)
405 uint8_t *d;
406 const uint8_t *font_ptr;
407 unsigned int font_data, linesize, xorcol, bpp;
408 int i;
409 unsigned int fgcol, bgcol;
411 #ifdef DEBUG_CONSOLE
412 printf("x: %2i y: %2i", x, y);
413 console_print_text_attributes(t_attrib, ch);
414 #endif
416 if (t_attrib->invers) {
417 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
418 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
419 } else {
420 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
421 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
424 bpp = (ds->depth + 7) >> 3;
425 d = ds->data +
426 ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
427 linesize = ds->linesize;
428 font_ptr = vgafont16 + FONT_HEIGHT * ch;
429 xorcol = bgcol ^ fgcol;
430 switch(ds->depth) {
431 case 8:
432 for(i = 0; i < FONT_HEIGHT; i++) {
433 font_data = *font_ptr++;
434 if (t_attrib->uline
435 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
436 font_data = 0xFFFF;
438 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
439 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
440 d += linesize;
442 break;
443 case 16:
444 case 15:
445 for(i = 0; i < FONT_HEIGHT; i++) {
446 font_data = *font_ptr++;
447 if (t_attrib->uline
448 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
449 font_data = 0xFFFF;
451 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
452 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
453 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
454 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
455 d += linesize;
457 break;
458 case 32:
459 for(i = 0; i < FONT_HEIGHT; i++) {
460 font_data = *font_ptr++;
461 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
462 font_data = 0xFFFF;
464 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
465 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
466 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
467 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
468 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
469 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
470 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
471 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
472 d += linesize;
474 break;
478 static void text_console_resize(TextConsole *s)
480 TextCell *cells, *c, *c1;
481 int w1, x, y, last_width;
483 last_width = s->width;
484 s->width = s->g_width / FONT_WIDTH;
485 s->height = s->g_height / FONT_HEIGHT;
487 w1 = last_width;
488 if (s->width < w1)
489 w1 = s->width;
491 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
492 for(y = 0; y < s->total_height; y++) {
493 c = &cells[y * s->width];
494 if (w1 > 0) {
495 c1 = &s->cells[y * last_width];
496 for(x = 0; x < w1; x++) {
497 *c++ = *c1++;
500 for(x = w1; x < s->width; x++) {
501 c->ch = ' ';
502 c->t_attrib = s->t_attrib_default;
503 c++;
506 free(s->cells);
507 s->cells = cells;
510 static void update_xy(TextConsole *s, int x, int y)
512 TextCell *c;
513 int y1, y2;
515 if (s == active_console) {
516 y1 = (s->y_base + y) % s->total_height;
517 y2 = y1 - s->y_displayed;
518 if (y2 < 0)
519 y2 += s->total_height;
520 if (y2 < s->height) {
521 c = &s->cells[y1 * s->width + x];
522 vga_putcharxy(s->ds, x, y2, c->ch,
523 &(c->t_attrib));
524 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
525 FONT_WIDTH, FONT_HEIGHT);
530 static void console_show_cursor(TextConsole *s, int show)
532 TextCell *c;
533 int y, y1;
535 if (s == active_console) {
536 y1 = (s->y_base + s->y) % s->total_height;
537 y = y1 - s->y_displayed;
538 if (y < 0)
539 y += s->total_height;
540 if (y < s->height) {
541 c = &s->cells[y1 * s->width + s->x];
542 if (show) {
543 TextAttributes t_attrib = s->t_attrib_default;
544 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
545 vga_putcharxy(s->ds, s->x, y, c->ch, &t_attrib);
546 } else {
547 vga_putcharxy(s->ds, s->x, y, c->ch,
548 &(c->t_attrib));
550 dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
551 FONT_WIDTH, FONT_HEIGHT);
556 static void console_refresh(TextConsole *s)
558 TextCell *c;
559 int x, y, y1;
561 if (s != active_console)
562 return;
564 vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
565 color_table[0][COLOR_BLACK]);
566 y1 = s->y_displayed;
567 for(y = 0; y < s->height; y++) {
568 c = s->cells + y1 * s->width;
569 for(x = 0; x < s->width; x++) {
570 vga_putcharxy(s->ds, x, y, c->ch,
571 &(c->t_attrib));
572 c++;
574 if (++y1 == s->total_height)
575 y1 = 0;
577 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
578 console_show_cursor(s, 1);
581 static void console_scroll(int ydelta)
583 TextConsole *s;
584 int i, y1;
586 s = active_console;
587 if (!s || !s->text_console)
588 return;
590 if (ydelta > 0) {
591 for(i = 0; i < ydelta; i++) {
592 if (s->y_displayed == s->y_base)
593 break;
594 if (++s->y_displayed == s->total_height)
595 s->y_displayed = 0;
597 } else {
598 ydelta = -ydelta;
599 i = s->backscroll_height;
600 if (i > s->total_height - s->height)
601 i = s->total_height - s->height;
602 y1 = s->y_base - i;
603 if (y1 < 0)
604 y1 += s->total_height;
605 for(i = 0; i < ydelta; i++) {
606 if (s->y_displayed == y1)
607 break;
608 if (--s->y_displayed < 0)
609 s->y_displayed = s->total_height - 1;
612 console_refresh(s);
615 static void console_put_lf(TextConsole *s)
617 TextCell *c;
618 int x, y1;
620 s->y++;
621 if (s->y >= s->height) {
622 s->y = s->height - 1;
624 if (s->y_displayed == s->y_base) {
625 if (++s->y_displayed == s->total_height)
626 s->y_displayed = 0;
628 if (++s->y_base == s->total_height)
629 s->y_base = 0;
630 if (s->backscroll_height < s->total_height)
631 s->backscroll_height++;
632 y1 = (s->y_base + s->height - 1) % s->total_height;
633 c = &s->cells[y1 * s->width];
634 for(x = 0; x < s->width; x++) {
635 c->ch = ' ';
636 c->t_attrib = s->t_attrib_default;
637 c++;
639 if (s == active_console && s->y_displayed == s->y_base) {
640 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
641 s->width * FONT_WIDTH,
642 (s->height - 1) * FONT_HEIGHT);
643 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
644 s->width * FONT_WIDTH, FONT_HEIGHT,
645 color_table[0][s->t_attrib_default.bgcol]);
646 dpy_update(s->ds, 0, 0,
647 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
652 /* Set console attributes depending on the current escape codes.
653 * NOTE: I know this code is not very efficient (checking every color for it
654 * self) but it is more readable and better maintainable.
656 static void console_handle_escape(TextConsole *s)
658 int i;
660 for (i=0; i<s->nb_esc_params; i++) {
661 switch (s->esc_params[i]) {
662 case 0: /* reset all console attributes to default */
663 s->t_attrib = s->t_attrib_default;
664 break;
665 case 1:
666 s->t_attrib.bold = 1;
667 break;
668 case 4:
669 s->t_attrib.uline = 1;
670 break;
671 case 5:
672 s->t_attrib.blink = 1;
673 break;
674 case 7:
675 s->t_attrib.invers = 1;
676 break;
677 case 8:
678 s->t_attrib.unvisible = 1;
679 break;
680 case 22:
681 s->t_attrib.bold = 0;
682 break;
683 case 24:
684 s->t_attrib.uline = 0;
685 break;
686 case 25:
687 s->t_attrib.blink = 0;
688 break;
689 case 27:
690 s->t_attrib.invers = 0;
691 break;
692 case 28:
693 s->t_attrib.unvisible = 0;
694 break;
695 /* set foreground color */
696 case 30:
697 s->t_attrib.fgcol=COLOR_BLACK;
698 break;
699 case 31:
700 s->t_attrib.fgcol=COLOR_RED;
701 break;
702 case 32:
703 s->t_attrib.fgcol=COLOR_GREEN;
704 break;
705 case 33:
706 s->t_attrib.fgcol=COLOR_YELLOW;
707 break;
708 case 34:
709 s->t_attrib.fgcol=COLOR_BLUE;
710 break;
711 case 35:
712 s->t_attrib.fgcol=COLOR_MAGENTA;
713 break;
714 case 36:
715 s->t_attrib.fgcol=COLOR_CYAN;
716 break;
717 case 37:
718 s->t_attrib.fgcol=COLOR_WHITE;
719 break;
720 /* set background color */
721 case 40:
722 s->t_attrib.bgcol=COLOR_BLACK;
723 break;
724 case 41:
725 s->t_attrib.bgcol=COLOR_RED;
726 break;
727 case 42:
728 s->t_attrib.bgcol=COLOR_GREEN;
729 break;
730 case 43:
731 s->t_attrib.bgcol=COLOR_YELLOW;
732 break;
733 case 44:
734 s->t_attrib.bgcol=COLOR_BLUE;
735 break;
736 case 45:
737 s->t_attrib.bgcol=COLOR_MAGENTA;
738 break;
739 case 46:
740 s->t_attrib.bgcol=COLOR_CYAN;
741 break;
742 case 47:
743 s->t_attrib.bgcol=COLOR_WHITE;
744 break;
749 static void console_clear_xy(TextConsole *s, int x, int y)
751 int y1 = (s->y_base + y) % s->total_height;
752 TextCell *c = &s->cells[y1 * s->width + x];
753 c->ch = ' ';
754 c->t_attrib = s->t_attrib_default;
755 c++;
756 update_xy(s, x, y);
759 static void console_putchar(TextConsole *s, int ch)
761 TextCell *c;
762 int y1, i;
763 int x, y;
765 switch(s->state) {
766 case TTY_STATE_NORM:
767 switch(ch) {
768 case '\r': /* carriage return */
769 s->x = 0;
770 break;
771 case '\n': /* newline */
772 console_put_lf(s);
773 break;
774 case '\b': /* backspace */
775 if (s->x > 0)
776 s->x--;
777 break;
778 case '\t': /* tabspace */
779 if (s->x + (8 - (s->x % 8)) > s->width) {
780 s->x = 0;
781 console_put_lf(s);
782 } else {
783 s->x = s->x + (8 - (s->x % 8));
785 break;
786 case '\a': /* alert aka. bell */
787 /* TODO: has to be implemented */
788 break;
789 case 14:
790 /* SI (shift in), character set 0 (ignored) */
791 break;
792 case 15:
793 /* SO (shift out), character set 1 (ignored) */
794 break;
795 case 27: /* esc (introducing an escape sequence) */
796 s->state = TTY_STATE_ESC;
797 break;
798 default:
799 if (s->x >= s->width - 1) {
800 break;
802 y1 = (s->y_base + s->y) % s->total_height;
803 c = &s->cells[y1 * s->width + s->x];
804 c->ch = ch;
805 c->t_attrib = s->t_attrib;
806 update_xy(s, s->x, s->y);
807 s->x++;
808 #if 0 /* line wrap disabled */
809 if (s->x >= s->width) {
810 s->x = 0;
811 console_put_lf(s);
813 #endif
814 break;
816 break;
817 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
818 if (ch == '[') {
819 for(i=0;i<MAX_ESC_PARAMS;i++)
820 s->esc_params[i] = 0;
821 s->nb_esc_params = 0;
822 s->state = TTY_STATE_CSI;
823 } else {
824 s->state = TTY_STATE_NORM;
826 break;
827 case TTY_STATE_CSI: /* handle escape sequence parameters */
828 if (ch >= '0' && ch <= '9') {
829 if (s->nb_esc_params < MAX_ESC_PARAMS) {
830 s->esc_params[s->nb_esc_params] =
831 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
833 } else {
834 s->nb_esc_params++;
835 if (ch == ';')
836 break;
837 #ifdef DEBUG_CONSOLE
838 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
839 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
840 #endif
841 s->state = TTY_STATE_NORM;
842 switch(ch) {
843 case 'A':
844 /* move cursor up */
845 if (s->esc_params[0] == 0) {
846 s->esc_params[0] = 1;
848 s->y -= s->esc_params[0];
849 if (s->y < 0) {
850 s->y = 0;
852 break;
853 case 'B':
854 /* move cursor down */
855 if (s->esc_params[0] == 0) {
856 s->esc_params[0] = 1;
858 s->y += s->esc_params[0];
859 if (s->y >= s->height) {
860 s->y = s->height - 1;
862 break;
863 case 'C':
864 /* move cursor right */
865 if (s->esc_params[0] == 0) {
866 s->esc_params[0] = 1;
868 s->x += s->esc_params[0];
869 if (s->x >= s->width) {
870 s->x = s->width - 1;
872 break;
873 case 'D':
874 /* move cursor left */
875 if (s->esc_params[0] == 0) {
876 s->esc_params[0] = 1;
878 s->x -= s->esc_params[0];
879 if (s->x < 0) {
880 s->x = 0;
882 break;
883 case 'G':
884 /* move cursor to column */
885 s->x = s->esc_params[0] - 1;
886 if (s->x < 0) {
887 s->x = 0;
889 break;
890 case 'f':
891 case 'H':
892 /* move cursor to row, column */
893 s->x = s->esc_params[1] - 1;
894 if (s->x < 0) {
895 s->x = 0;
897 s->y = s->esc_params[0] - 1;
898 if (s->y < 0) {
899 s->y = 0;
901 break;
902 case 'J':
903 switch (s->esc_params[0]) {
904 case 0:
905 /* clear to end of screen */
906 for (y = s->y; y < s->height; y++) {
907 for (x = 0; x < s->width; x++) {
908 if (y == s->y && x < s->x) {
909 continue;
911 console_clear_xy(s, x, y);
914 break;
915 case 1:
916 /* clear from beginning of screen */
917 for (y = 0; y <= s->y; y++) {
918 for (x = 0; x < s->width; x++) {
919 if (y == s->y && x > s->x) {
920 break;
922 console_clear_xy(s, x, y);
925 break;
926 case 2:
927 /* clear entire screen */
928 for (y = 0; y <= s->height; y++) {
929 for (x = 0; x < s->width; x++) {
930 console_clear_xy(s, x, y);
933 break;
935 case 'K':
936 switch (s->esc_params[0]) {
937 case 0:
938 /* clear to eol */
939 for(x = s->x; x < s->width; x++) {
940 console_clear_xy(s, x, s->y);
942 break;
943 case 1:
944 /* clear from beginning of line */
945 for (x = 0; x <= s->x; x++) {
946 console_clear_xy(s, x, s->y);
948 break;
949 case 2:
950 /* clear entire line */
951 for(x = 0; x < s->width; x++) {
952 console_clear_xy(s, x, s->y);
954 break;
956 break;
957 case 'm':
958 console_handle_escape(s);
959 break;
960 case 'n':
961 /* report cursor position */
962 /* TODO: send ESC[row;colR */
963 break;
964 case 's':
965 /* save cursor position */
966 s->x_saved = s->x;
967 s->y_saved = s->y;
968 break;
969 case 'u':
970 /* restore cursor position */
971 s->x = s->x_saved;
972 s->y = s->y_saved;
973 break;
974 default:
975 #ifdef DEBUG_CONSOLE
976 fprintf(stderr, "unhandled escape character '%c'\n", ch);
977 #endif
978 break;
980 break;
985 void console_select(unsigned int index)
987 TextConsole *s;
989 if (index >= MAX_CONSOLES)
990 return;
991 s = consoles[index];
992 if (s) {
993 active_console = s;
994 if (s->text_console) {
995 if (s->g_width != s->ds->width ||
996 s->g_height != s->ds->height) {
997 s->g_width = s->ds->width;
998 s->g_height = s->ds->height;
999 text_console_resize(s);
1001 console_refresh(s);
1002 } else {
1003 vga_hw_invalidate();
1008 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1010 TextConsole *s = chr->opaque;
1011 int i;
1013 console_show_cursor(s, 0);
1014 for(i = 0; i < len; i++) {
1015 console_putchar(s, buf[i]);
1017 console_show_cursor(s, 1);
1018 return len;
1021 static void console_send_event(CharDriverState *chr, int event)
1023 TextConsole *s = chr->opaque;
1024 int i;
1026 if (event == CHR_EVENT_FOCUS) {
1027 for(i = 0; i < nb_consoles; i++) {
1028 if (consoles[i] == s) {
1029 console_select(i);
1030 break;
1036 static void kbd_send_chars(void *opaque)
1038 TextConsole *s = opaque;
1039 int len;
1040 uint8_t buf[16];
1042 len = qemu_chr_can_read(s->chr);
1043 if (len > s->out_fifo.count)
1044 len = s->out_fifo.count;
1045 if (len > 0) {
1046 if (len > sizeof(buf))
1047 len = sizeof(buf);
1048 qemu_fifo_read(&s->out_fifo, buf, len);
1049 qemu_chr_read(s->chr, buf, len);
1051 /* characters are pending: we send them a bit later (XXX:
1052 horrible, should change char device API) */
1053 if (s->out_fifo.count > 0) {
1054 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1058 /* called when an ascii key is pressed */
1059 void kbd_put_keysym(int keysym)
1061 TextConsole *s;
1062 uint8_t buf[16], *q;
1063 int c;
1065 s = active_console;
1066 if (!s || !s->text_console)
1067 return;
1069 switch(keysym) {
1070 case QEMU_KEY_CTRL_UP:
1071 console_scroll(-1);
1072 break;
1073 case QEMU_KEY_CTRL_DOWN:
1074 console_scroll(1);
1075 break;
1076 case QEMU_KEY_CTRL_PAGEUP:
1077 console_scroll(-10);
1078 break;
1079 case QEMU_KEY_CTRL_PAGEDOWN:
1080 console_scroll(10);
1081 break;
1082 default:
1083 /* convert the QEMU keysym to VT100 key string */
1084 q = buf;
1085 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1086 *q++ = '\033';
1087 *q++ = '[';
1088 c = keysym - 0xe100;
1089 if (c >= 10)
1090 *q++ = '0' + (c / 10);
1091 *q++ = '0' + (c % 10);
1092 *q++ = '~';
1093 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1094 *q++ = '\033';
1095 *q++ = '[';
1096 *q++ = keysym & 0xff;
1097 } else {
1098 *q++ = keysym;
1100 if (s->chr->chr_read) {
1101 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1102 kbd_send_chars(s);
1104 break;
1108 static TextConsole *new_console(DisplayState *ds, int text)
1110 TextConsole *s;
1111 int i;
1113 if (nb_consoles >= MAX_CONSOLES)
1114 return NULL;
1115 s = qemu_mallocz(sizeof(TextConsole));
1116 if (!s) {
1117 return NULL;
1119 if (!active_console || (active_console->text_console && !text))
1120 active_console = s;
1121 s->ds = ds;
1122 s->text_console = text;
1123 if (text) {
1124 consoles[nb_consoles++] = s;
1125 } else {
1126 /* HACK: Put graphical consoles before text consoles. */
1127 for (i = nb_consoles; i > 0; i--) {
1128 if (!consoles[i - 1]->text_console)
1129 break;
1130 consoles[i] = consoles[i - 1];
1132 consoles[i] = s;
1134 return s;
1137 TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1138 vga_hw_invalidate_ptr invalidate,
1139 vga_hw_screen_dump_ptr screen_dump,
1140 void *opaque)
1142 TextConsole *s;
1144 s = new_console(ds, 0);
1145 if (!s)
1146 return NULL;
1147 s->hw_update = update;
1148 s->hw_invalidate = invalidate;
1149 s->hw_screen_dump = screen_dump;
1150 s->hw = opaque;
1151 return s;
1154 int is_graphic_console(void)
1156 return !active_console->text_console;
1159 CharDriverState *text_console_init(DisplayState *ds)
1161 CharDriverState *chr;
1162 TextConsole *s;
1163 int i,j;
1164 static int color_inited;
1166 chr = qemu_mallocz(sizeof(CharDriverState));
1167 if (!chr)
1168 return NULL;
1169 s = new_console(ds, 1);
1170 if (!s) {
1171 free(chr);
1172 return NULL;
1174 chr->opaque = s;
1175 chr->chr_write = console_puts;
1176 chr->chr_send_event = console_send_event;
1178 s->chr = chr;
1179 s->out_fifo.buf = s->out_fifo_buf;
1180 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1181 s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1183 if (!color_inited) {
1184 color_inited = 1;
1185 for(j = 0; j < 2; j++) {
1186 for(i = 0; i < 8; i++) {
1187 color_table[j][i] = col_expand(s->ds,
1188 vga_get_color(s->ds, color_table_rgb[j][i]));
1192 s->y_displayed = 0;
1193 s->y_base = 0;
1194 s->total_height = DEFAULT_BACKSCROLL;
1195 s->x = 0;
1196 s->y = 0;
1197 s->g_width = s->ds->width;
1198 s->g_height = s->ds->height;
1200 /* Set text attribute defaults */
1201 s->t_attrib_default.bold = 0;
1202 s->t_attrib_default.uline = 0;
1203 s->t_attrib_default.blink = 0;
1204 s->t_attrib_default.invers = 0;
1205 s->t_attrib_default.unvisible = 0;
1206 s->t_attrib_default.fgcol = COLOR_WHITE;
1207 s->t_attrib_default.bgcol = COLOR_BLACK;
1209 /* set current text attributes to default */
1210 s->t_attrib = s->t_attrib_default;
1211 text_console_resize(s);
1213 qemu_chr_reset(chr);
1215 return chr;