SD card emulation (initial implementation by Andrzei Zaborowski).
[qemu/mini2440.git] / console.c
bloba53b30177427415340a71dc9c3d6aed08b5764b2
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 int x = s->x;
537 if (x >= s->width) {
538 x = s->width - 1;
540 y1 = (s->y_base + s->y) % s->total_height;
541 y = y1 - s->y_displayed;
542 if (y < 0)
543 y += s->total_height;
544 if (y < s->height) {
545 c = &s->cells[y1 * s->width + x];
546 if (show) {
547 TextAttributes t_attrib = s->t_attrib_default;
548 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
549 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
550 } else {
551 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
553 dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
554 FONT_WIDTH, FONT_HEIGHT);
559 static void console_refresh(TextConsole *s)
561 TextCell *c;
562 int x, y, y1;
564 if (s != active_console)
565 return;
567 vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
568 color_table[0][COLOR_BLACK]);
569 y1 = s->y_displayed;
570 for(y = 0; y < s->height; y++) {
571 c = s->cells + y1 * s->width;
572 for(x = 0; x < s->width; x++) {
573 vga_putcharxy(s->ds, x, y, c->ch,
574 &(c->t_attrib));
575 c++;
577 if (++y1 == s->total_height)
578 y1 = 0;
580 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
581 console_show_cursor(s, 1);
584 static void console_scroll(int ydelta)
586 TextConsole *s;
587 int i, y1;
589 s = active_console;
590 if (!s || !s->text_console)
591 return;
593 if (ydelta > 0) {
594 for(i = 0; i < ydelta; i++) {
595 if (s->y_displayed == s->y_base)
596 break;
597 if (++s->y_displayed == s->total_height)
598 s->y_displayed = 0;
600 } else {
601 ydelta = -ydelta;
602 i = s->backscroll_height;
603 if (i > s->total_height - s->height)
604 i = s->total_height - s->height;
605 y1 = s->y_base - i;
606 if (y1 < 0)
607 y1 += s->total_height;
608 for(i = 0; i < ydelta; i++) {
609 if (s->y_displayed == y1)
610 break;
611 if (--s->y_displayed < 0)
612 s->y_displayed = s->total_height - 1;
615 console_refresh(s);
618 static void console_put_lf(TextConsole *s)
620 TextCell *c;
621 int x, y1;
623 s->y++;
624 if (s->y >= s->height) {
625 s->y = s->height - 1;
627 if (s->y_displayed == s->y_base) {
628 if (++s->y_displayed == s->total_height)
629 s->y_displayed = 0;
631 if (++s->y_base == s->total_height)
632 s->y_base = 0;
633 if (s->backscroll_height < s->total_height)
634 s->backscroll_height++;
635 y1 = (s->y_base + s->height - 1) % s->total_height;
636 c = &s->cells[y1 * s->width];
637 for(x = 0; x < s->width; x++) {
638 c->ch = ' ';
639 c->t_attrib = s->t_attrib_default;
640 c++;
642 if (s == active_console && s->y_displayed == s->y_base) {
643 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
644 s->width * FONT_WIDTH,
645 (s->height - 1) * FONT_HEIGHT);
646 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
647 s->width * FONT_WIDTH, FONT_HEIGHT,
648 color_table[0][s->t_attrib_default.bgcol]);
649 dpy_update(s->ds, 0, 0,
650 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
655 /* Set console attributes depending on the current escape codes.
656 * NOTE: I know this code is not very efficient (checking every color for it
657 * self) but it is more readable and better maintainable.
659 static void console_handle_escape(TextConsole *s)
661 int i;
663 for (i=0; i<s->nb_esc_params; i++) {
664 switch (s->esc_params[i]) {
665 case 0: /* reset all console attributes to default */
666 s->t_attrib = s->t_attrib_default;
667 break;
668 case 1:
669 s->t_attrib.bold = 1;
670 break;
671 case 4:
672 s->t_attrib.uline = 1;
673 break;
674 case 5:
675 s->t_attrib.blink = 1;
676 break;
677 case 7:
678 s->t_attrib.invers = 1;
679 break;
680 case 8:
681 s->t_attrib.unvisible = 1;
682 break;
683 case 22:
684 s->t_attrib.bold = 0;
685 break;
686 case 24:
687 s->t_attrib.uline = 0;
688 break;
689 case 25:
690 s->t_attrib.blink = 0;
691 break;
692 case 27:
693 s->t_attrib.invers = 0;
694 break;
695 case 28:
696 s->t_attrib.unvisible = 0;
697 break;
698 /* set foreground color */
699 case 30:
700 s->t_attrib.fgcol=COLOR_BLACK;
701 break;
702 case 31:
703 s->t_attrib.fgcol=COLOR_RED;
704 break;
705 case 32:
706 s->t_attrib.fgcol=COLOR_GREEN;
707 break;
708 case 33:
709 s->t_attrib.fgcol=COLOR_YELLOW;
710 break;
711 case 34:
712 s->t_attrib.fgcol=COLOR_BLUE;
713 break;
714 case 35:
715 s->t_attrib.fgcol=COLOR_MAGENTA;
716 break;
717 case 36:
718 s->t_attrib.fgcol=COLOR_CYAN;
719 break;
720 case 37:
721 s->t_attrib.fgcol=COLOR_WHITE;
722 break;
723 /* set background color */
724 case 40:
725 s->t_attrib.bgcol=COLOR_BLACK;
726 break;
727 case 41:
728 s->t_attrib.bgcol=COLOR_RED;
729 break;
730 case 42:
731 s->t_attrib.bgcol=COLOR_GREEN;
732 break;
733 case 43:
734 s->t_attrib.bgcol=COLOR_YELLOW;
735 break;
736 case 44:
737 s->t_attrib.bgcol=COLOR_BLUE;
738 break;
739 case 45:
740 s->t_attrib.bgcol=COLOR_MAGENTA;
741 break;
742 case 46:
743 s->t_attrib.bgcol=COLOR_CYAN;
744 break;
745 case 47:
746 s->t_attrib.bgcol=COLOR_WHITE;
747 break;
752 static void console_clear_xy(TextConsole *s, int x, int y)
754 int y1 = (s->y_base + y) % s->total_height;
755 TextCell *c = &s->cells[y1 * s->width + x];
756 c->ch = ' ';
757 c->t_attrib = s->t_attrib_default;
758 c++;
759 update_xy(s, x, y);
762 static void console_putchar(TextConsole *s, int ch)
764 TextCell *c;
765 int y1, i;
766 int x, y;
768 switch(s->state) {
769 case TTY_STATE_NORM:
770 switch(ch) {
771 case '\r': /* carriage return */
772 s->x = 0;
773 break;
774 case '\n': /* newline */
775 console_put_lf(s);
776 break;
777 case '\b': /* backspace */
778 if (s->x > 0)
779 s->x--;
780 break;
781 case '\t': /* tabspace */
782 if (s->x + (8 - (s->x % 8)) > s->width) {
783 s->x = 0;
784 console_put_lf(s);
785 } else {
786 s->x = s->x + (8 - (s->x % 8));
788 break;
789 case '\a': /* alert aka. bell */
790 /* TODO: has to be implemented */
791 break;
792 case 14:
793 /* SI (shift in), character set 0 (ignored) */
794 break;
795 case 15:
796 /* SO (shift out), character set 1 (ignored) */
797 break;
798 case 27: /* esc (introducing an escape sequence) */
799 s->state = TTY_STATE_ESC;
800 break;
801 default:
802 if (s->x >= s->width) {
803 /* line wrap */
804 s->x = 0;
805 console_put_lf(s);
807 y1 = (s->y_base + s->y) % s->total_height;
808 c = &s->cells[y1 * s->width + s->x];
809 c->ch = ch;
810 c->t_attrib = s->t_attrib;
811 update_xy(s, s->x, s->y);
812 s->x++;
813 break;
815 break;
816 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
817 if (ch == '[') {
818 for(i=0;i<MAX_ESC_PARAMS;i++)
819 s->esc_params[i] = 0;
820 s->nb_esc_params = 0;
821 s->state = TTY_STATE_CSI;
822 } else {
823 s->state = TTY_STATE_NORM;
825 break;
826 case TTY_STATE_CSI: /* handle escape sequence parameters */
827 if (ch >= '0' && ch <= '9') {
828 if (s->nb_esc_params < MAX_ESC_PARAMS) {
829 s->esc_params[s->nb_esc_params] =
830 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
832 } else {
833 s->nb_esc_params++;
834 if (ch == ';')
835 break;
836 #ifdef DEBUG_CONSOLE
837 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
838 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
839 #endif
840 s->state = TTY_STATE_NORM;
841 switch(ch) {
842 case 'A':
843 /* move cursor up */
844 if (s->esc_params[0] == 0) {
845 s->esc_params[0] = 1;
847 s->y -= s->esc_params[0];
848 if (s->y < 0) {
849 s->y = 0;
851 break;
852 case 'B':
853 /* move cursor down */
854 if (s->esc_params[0] == 0) {
855 s->esc_params[0] = 1;
857 s->y += s->esc_params[0];
858 if (s->y >= s->height) {
859 s->y = s->height - 1;
861 break;
862 case 'C':
863 /* move cursor right */
864 if (s->esc_params[0] == 0) {
865 s->esc_params[0] = 1;
867 s->x += s->esc_params[0];
868 if (s->x >= s->width) {
869 s->x = s->width - 1;
871 break;
872 case 'D':
873 /* move cursor left */
874 if (s->esc_params[0] == 0) {
875 s->esc_params[0] = 1;
877 s->x -= s->esc_params[0];
878 if (s->x < 0) {
879 s->x = 0;
881 break;
882 case 'G':
883 /* move cursor to column */
884 s->x = s->esc_params[0] - 1;
885 if (s->x < 0) {
886 s->x = 0;
888 break;
889 case 'f':
890 case 'H':
891 /* move cursor to row, column */
892 s->x = s->esc_params[1] - 1;
893 if (s->x < 0) {
894 s->x = 0;
896 s->y = s->esc_params[0] - 1;
897 if (s->y < 0) {
898 s->y = 0;
900 break;
901 case 'J':
902 switch (s->esc_params[0]) {
903 case 0:
904 /* clear to end of screen */
905 for (y = s->y; y < s->height; y++) {
906 for (x = 0; x < s->width; x++) {
907 if (y == s->y && x < s->x) {
908 continue;
910 console_clear_xy(s, x, y);
913 break;
914 case 1:
915 /* clear from beginning of screen */
916 for (y = 0; y <= s->y; y++) {
917 for (x = 0; x < s->width; x++) {
918 if (y == s->y && x > s->x) {
919 break;
921 console_clear_xy(s, x, y);
924 break;
925 case 2:
926 /* clear entire screen */
927 for (y = 0; y <= s->height; y++) {
928 for (x = 0; x < s->width; x++) {
929 console_clear_xy(s, x, y);
932 break;
934 case 'K':
935 switch (s->esc_params[0]) {
936 case 0:
937 /* clear to eol */
938 for(x = s->x; x < s->width; x++) {
939 console_clear_xy(s, x, s->y);
941 break;
942 case 1:
943 /* clear from beginning of line */
944 for (x = 0; x <= s->x; x++) {
945 console_clear_xy(s, x, s->y);
947 break;
948 case 2:
949 /* clear entire line */
950 for(x = 0; x < s->width; x++) {
951 console_clear_xy(s, x, s->y);
953 break;
955 break;
956 case 'm':
957 console_handle_escape(s);
958 break;
959 case 'n':
960 /* report cursor position */
961 /* TODO: send ESC[row;colR */
962 break;
963 case 's':
964 /* save cursor position */
965 s->x_saved = s->x;
966 s->y_saved = s->y;
967 break;
968 case 'u':
969 /* restore cursor position */
970 s->x = s->x_saved;
971 s->y = s->y_saved;
972 break;
973 default:
974 #ifdef DEBUG_CONSOLE
975 fprintf(stderr, "unhandled escape character '%c'\n", ch);
976 #endif
977 break;
979 break;
984 void console_select(unsigned int index)
986 TextConsole *s;
988 if (index >= MAX_CONSOLES)
989 return;
990 s = consoles[index];
991 if (s) {
992 active_console = s;
993 if (s->text_console) {
994 if (s->g_width != s->ds->width ||
995 s->g_height != s->ds->height) {
996 s->g_width = s->ds->width;
997 s->g_height = s->ds->height;
998 text_console_resize(s);
1000 console_refresh(s);
1001 } else {
1002 vga_hw_invalidate();
1007 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1009 TextConsole *s = chr->opaque;
1010 int i;
1012 console_show_cursor(s, 0);
1013 for(i = 0; i < len; i++) {
1014 console_putchar(s, buf[i]);
1016 console_show_cursor(s, 1);
1017 return len;
1020 static void console_send_event(CharDriverState *chr, int event)
1022 TextConsole *s = chr->opaque;
1023 int i;
1025 if (event == CHR_EVENT_FOCUS) {
1026 for(i = 0; i < nb_consoles; i++) {
1027 if (consoles[i] == s) {
1028 console_select(i);
1029 break;
1035 static void kbd_send_chars(void *opaque)
1037 TextConsole *s = opaque;
1038 int len;
1039 uint8_t buf[16];
1041 len = qemu_chr_can_read(s->chr);
1042 if (len > s->out_fifo.count)
1043 len = s->out_fifo.count;
1044 if (len > 0) {
1045 if (len > sizeof(buf))
1046 len = sizeof(buf);
1047 qemu_fifo_read(&s->out_fifo, buf, len);
1048 qemu_chr_read(s->chr, buf, len);
1050 /* characters are pending: we send them a bit later (XXX:
1051 horrible, should change char device API) */
1052 if (s->out_fifo.count > 0) {
1053 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1057 /* called when an ascii key is pressed */
1058 void kbd_put_keysym(int keysym)
1060 TextConsole *s;
1061 uint8_t buf[16], *q;
1062 int c;
1064 s = active_console;
1065 if (!s || !s->text_console)
1066 return;
1068 switch(keysym) {
1069 case QEMU_KEY_CTRL_UP:
1070 console_scroll(-1);
1071 break;
1072 case QEMU_KEY_CTRL_DOWN:
1073 console_scroll(1);
1074 break;
1075 case QEMU_KEY_CTRL_PAGEUP:
1076 console_scroll(-10);
1077 break;
1078 case QEMU_KEY_CTRL_PAGEDOWN:
1079 console_scroll(10);
1080 break;
1081 default:
1082 /* convert the QEMU keysym to VT100 key string */
1083 q = buf;
1084 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1085 *q++ = '\033';
1086 *q++ = '[';
1087 c = keysym - 0xe100;
1088 if (c >= 10)
1089 *q++ = '0' + (c / 10);
1090 *q++ = '0' + (c % 10);
1091 *q++ = '~';
1092 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1093 *q++ = '\033';
1094 *q++ = '[';
1095 *q++ = keysym & 0xff;
1096 } else {
1097 *q++ = keysym;
1099 if (s->chr->chr_read) {
1100 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1101 kbd_send_chars(s);
1103 break;
1107 static TextConsole *new_console(DisplayState *ds, int text)
1109 TextConsole *s;
1110 int i;
1112 if (nb_consoles >= MAX_CONSOLES)
1113 return NULL;
1114 s = qemu_mallocz(sizeof(TextConsole));
1115 if (!s) {
1116 return NULL;
1118 if (!active_console || (active_console->text_console && !text))
1119 active_console = s;
1120 s->ds = ds;
1121 s->text_console = text;
1122 if (text) {
1123 consoles[nb_consoles++] = s;
1124 } else {
1125 /* HACK: Put graphical consoles before text consoles. */
1126 for (i = nb_consoles; i > 0; i--) {
1127 if (!consoles[i - 1]->text_console)
1128 break;
1129 consoles[i] = consoles[i - 1];
1131 consoles[i] = s;
1133 return s;
1136 TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1137 vga_hw_invalidate_ptr invalidate,
1138 vga_hw_screen_dump_ptr screen_dump,
1139 void *opaque)
1141 TextConsole *s;
1143 s = new_console(ds, 0);
1144 if (!s)
1145 return NULL;
1146 s->hw_update = update;
1147 s->hw_invalidate = invalidate;
1148 s->hw_screen_dump = screen_dump;
1149 s->hw = opaque;
1150 return s;
1153 int is_graphic_console(void)
1155 return !active_console->text_console;
1158 CharDriverState *text_console_init(DisplayState *ds)
1160 CharDriverState *chr;
1161 TextConsole *s;
1162 int i,j;
1163 static int color_inited;
1165 chr = qemu_mallocz(sizeof(CharDriverState));
1166 if (!chr)
1167 return NULL;
1168 s = new_console(ds, 1);
1169 if (!s) {
1170 free(chr);
1171 return NULL;
1173 chr->opaque = s;
1174 chr->chr_write = console_puts;
1175 chr->chr_send_event = console_send_event;
1177 s->chr = chr;
1178 s->out_fifo.buf = s->out_fifo_buf;
1179 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1180 s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1182 if (!color_inited) {
1183 color_inited = 1;
1184 for(j = 0; j < 2; j++) {
1185 for(i = 0; i < 8; i++) {
1186 color_table[j][i] = col_expand(s->ds,
1187 vga_get_color(s->ds, color_table_rgb[j][i]));
1191 s->y_displayed = 0;
1192 s->y_base = 0;
1193 s->total_height = DEFAULT_BACKSCROLL;
1194 s->x = 0;
1195 s->y = 0;
1196 s->g_width = s->ds->width;
1197 s->g_height = s->ds->height;
1199 /* Set text attribute defaults */
1200 s->t_attrib_default.bold = 0;
1201 s->t_attrib_default.uline = 0;
1202 s->t_attrib_default.blink = 0;
1203 s->t_attrib_default.invers = 0;
1204 s->t_attrib_default.unvisible = 0;
1205 s->t_attrib_default.fgcol = COLOR_WHITE;
1206 s->t_attrib_default.bgcol = COLOR_BLACK;
1208 /* set current text attributes to default */
1209 s->t_attrib = s->t_attrib_default;
1210 text_console_resize(s);
1212 qemu_chr_reset(chr);
1214 return chr;