]> git.proxmox.com Git - qemu.git/blob - console.c
b0e7f2e0d6271576aae5bd4c397b075cd98002ab
[qemu.git] / 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 "vl.h"
25
26 //#define DEBUG_CONSOLE
27 #define DEFAULT_BACKSCROLL 512
28 #define MAX_CONSOLES 12
29
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)
32
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;
42
43 typedef struct TextCell {
44 uint8_t ch;
45 TextAttributes t_attrib;
46 } TextCell;
47
48 #define MAX_ESC_PARAMS 3
49
50 enum TTYState {
51 TTY_STATE_NORM,
52 TTY_STATE_ESC,
53 TTY_STATE_CSI,
54 };
55
56 typedef struct QEMUFIFO {
57 uint8_t *buf;
58 int buf_size;
59 int count, wptr, rptr;
60 } QEMUFIFO;
61
62 int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
63 {
64 int l, len;
65
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;
80 }
81 f->count += len1;
82 return len1;
83 }
84
85 int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
86 {
87 int l, len;
88
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;
102 }
103 f->count -= len1;
104 return len1;
105 }
106
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;
117
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;
130
131 enum TTYState state;
132 int esc_params[MAX_ESC_PARAMS];
133 int nb_esc_params;
134
135 /* kbd read handler */
136 IOCanRWHandler *fd_can_read;
137 IOReadHandler *fd_read;
138 void *fd_opaque;
139 /* fifo for key pressed */
140 QEMUFIFO out_fifo;
141 uint8_t out_fifo_buf[16];
142 QEMUTimer *kbd_timer;
143 };
144
145 static TextConsole *active_console;
146 static TextConsole *consoles[MAX_CONSOLES];
147 static int nb_consoles = 0;
148
149 void vga_hw_update(void)
150 {
151 if (active_console && active_console->hw_update)
152 active_console->hw_update(active_console->hw);
153 }
154
155 void vga_hw_invalidate(void)
156 {
157 if (active_console->hw_invalidate)
158 active_console->hw_invalidate(active_console->hw);
159 }
160
161 void vga_hw_screen_dump(const char *filename)
162 {
163 /* There is currently no was of specifying which screen we want to dump,
164 so always dump the dirst one. */
165 if (consoles[0]->hw_screen_dump)
166 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
167 }
168
169 /* convert a RGBA color to a color index usable in graphic primitives */
170 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
171 {
172 unsigned int r, g, b, color;
173
174 switch(ds->depth) {
175 #if 0
176 case 8:
177 r = (rgba >> 16) & 0xff;
178 g = (rgba >> 8) & 0xff;
179 b = (rgba) & 0xff;
180 color = (rgb_to_index[r] * 6 * 6) +
181 (rgb_to_index[g] * 6) +
182 (rgb_to_index[b]);
183 break;
184 #endif
185 case 15:
186 r = (rgba >> 16) & 0xff;
187 g = (rgba >> 8) & 0xff;
188 b = (rgba) & 0xff;
189 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
190 break;
191 case 16:
192 r = (rgba >> 16) & 0xff;
193 g = (rgba >> 8) & 0xff;
194 b = (rgba) & 0xff;
195 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
196 break;
197 case 32:
198 default:
199 color = rgba;
200 break;
201 }
202 return color;
203 }
204
205 static void vga_fill_rect (DisplayState *ds,
206 int posx, int posy, int width, int height, uint32_t color)
207 {
208 uint8_t *d, *d1;
209 int x, y, bpp;
210
211 bpp = (ds->depth + 7) >> 3;
212 d1 = ds->data +
213 ds->linesize * posy + bpp * posx;
214 for (y = 0; y < height; y++) {
215 d = d1;
216 switch(bpp) {
217 case 1:
218 for (x = 0; x < width; x++) {
219 *((uint8_t *)d) = color;
220 d++;
221 }
222 break;
223 case 2:
224 for (x = 0; x < width; x++) {
225 *((uint16_t *)d) = color;
226 d += 2;
227 }
228 break;
229 case 4:
230 for (x = 0; x < width; x++) {
231 *((uint32_t *)d) = color;
232 d += 4;
233 }
234 break;
235 }
236 d1 += ds->linesize;
237 }
238 }
239
240 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
241 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
242 {
243 const uint8_t *s;
244 uint8_t *d;
245 int wb, y, bpp;
246
247 bpp = (ds->depth + 7) >> 3;
248 wb = w * bpp;
249 if (yd <= ys) {
250 s = ds->data +
251 ds->linesize * ys + bpp * xs;
252 d = ds->data +
253 ds->linesize * yd + bpp * xd;
254 for (y = 0; y < h; y++) {
255 memmove(d, s, wb);
256 d += ds->linesize;
257 s += ds->linesize;
258 }
259 } else {
260 s = ds->data +
261 ds->linesize * (ys + h - 1) + bpp * xs;
262 d = ds->data +
263 ds->linesize * (yd + h - 1) + bpp * xd;
264 for (y = 0; y < h; y++) {
265 memmove(d, s, wb);
266 d -= ds->linesize;
267 s -= ds->linesize;
268 }
269 }
270 }
271
272 /***********************************************************/
273 /* basic char display */
274
275 #define FONT_HEIGHT 16
276 #define FONT_WIDTH 8
277
278 #include "vgafont.h"
279
280 #define cbswap_32(__x) \
281 ((uint32_t)( \
282 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
283 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
284 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
285 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
286
287 #ifdef WORDS_BIGENDIAN
288 #define PAT(x) x
289 #else
290 #define PAT(x) cbswap_32(x)
291 #endif
292
293 static const uint32_t dmask16[16] = {
294 PAT(0x00000000),
295 PAT(0x000000ff),
296 PAT(0x0000ff00),
297 PAT(0x0000ffff),
298 PAT(0x00ff0000),
299 PAT(0x00ff00ff),
300 PAT(0x00ffff00),
301 PAT(0x00ffffff),
302 PAT(0xff000000),
303 PAT(0xff0000ff),
304 PAT(0xff00ff00),
305 PAT(0xff00ffff),
306 PAT(0xffff0000),
307 PAT(0xffff00ff),
308 PAT(0xffffff00),
309 PAT(0xffffffff),
310 };
311
312 static const uint32_t dmask4[4] = {
313 PAT(0x00000000),
314 PAT(0x0000ffff),
315 PAT(0xffff0000),
316 PAT(0xffffffff),
317 };
318
319 static uint32_t color_table[2][8];
320
321 enum color_names {
322 COLOR_BLACK = 0,
323 COLOR_RED = 1,
324 COLOR_GREEN = 2,
325 COLOR_YELLOW = 3,
326 COLOR_BLUE = 4,
327 COLOR_MAGENTA = 5,
328 COLOR_CYAN = 6,
329 COLOR_WHITE = 7
330 };
331
332 static const uint32_t color_table_rgb[2][8] = {
333 { /* dark */
334 QEMU_RGB(0x00, 0x00, 0x00), /* black */
335 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
336 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
337 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
338 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
339 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
340 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
341 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
342 },
343 { /* bright */
344 QEMU_RGB(0x00, 0x00, 0x00), /* black */
345 QEMU_RGB(0xff, 0x00, 0x00), /* red */
346 QEMU_RGB(0x00, 0xff, 0x00), /* green */
347 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
348 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
349 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
350 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
351 QEMU_RGB(0xff, 0xff, 0xff), /* white */
352 }
353 };
354
355 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
356 {
357 switch(ds->depth) {
358 case 8:
359 col |= col << 8;
360 col |= col << 16;
361 break;
362 case 15:
363 case 16:
364 col |= col << 16;
365 break;
366 default:
367 break;
368 }
369
370 return col;
371 }
372 #ifdef DEBUG_CONSOLE
373 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
374 {
375 if (t_attrib->bold) {
376 printf("b");
377 } else {
378 printf(" ");
379 }
380 if (t_attrib->uline) {
381 printf("u");
382 } else {
383 printf(" ");
384 }
385 if (t_attrib->blink) {
386 printf("l");
387 } else {
388 printf(" ");
389 }
390 if (t_attrib->invers) {
391 printf("i");
392 } else {
393 printf(" ");
394 }
395 if (t_attrib->unvisible) {
396 printf("n");
397 } else {
398 printf(" ");
399 }
400
401 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
402 }
403 #endif
404
405 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
406 TextAttributes *t_attrib)
407 {
408 uint8_t *d;
409 const uint8_t *font_ptr;
410 unsigned int font_data, linesize, xorcol, bpp;
411 int i;
412 unsigned int fgcol, bgcol;
413
414 #ifdef DEBUG_CONSOLE
415 printf("x: %2i y: %2i", x, y);
416 console_print_text_attributes(t_attrib, ch);
417 #endif
418
419 if (t_attrib->invers) {
420 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
421 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
422 } else {
423 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
424 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
425 }
426
427 bpp = (ds->depth + 7) >> 3;
428 d = ds->data +
429 ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
430 linesize = ds->linesize;
431 font_ptr = vgafont16 + FONT_HEIGHT * ch;
432 xorcol = bgcol ^ fgcol;
433 switch(ds->depth) {
434 case 8:
435 for(i = 0; i < FONT_HEIGHT; i++) {
436 font_data = *font_ptr++;
437 if (t_attrib->uline
438 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
439 font_data = 0xFFFF;
440 }
441 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
442 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
443 d += linesize;
444 }
445 break;
446 case 16:
447 case 15:
448 for(i = 0; i < FONT_HEIGHT; i++) {
449 font_data = *font_ptr++;
450 if (t_attrib->uline
451 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
452 font_data = 0xFFFF;
453 }
454 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
455 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
456 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
457 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
458 d += linesize;
459 }
460 break;
461 case 32:
462 for(i = 0; i < FONT_HEIGHT; i++) {
463 font_data = *font_ptr++;
464 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
465 font_data = 0xFFFF;
466 }
467 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
468 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
469 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
470 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
471 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
472 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
473 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
474 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
475 d += linesize;
476 }
477 break;
478 }
479 }
480
481 static void text_console_resize(TextConsole *s)
482 {
483 TextCell *cells, *c, *c1;
484 int w1, x, y, last_width;
485
486 last_width = s->width;
487 s->width = s->g_width / FONT_WIDTH;
488 s->height = s->g_height / FONT_HEIGHT;
489
490 w1 = last_width;
491 if (s->width < w1)
492 w1 = s->width;
493
494 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
495 for(y = 0; y < s->total_height; y++) {
496 c = &cells[y * s->width];
497 if (w1 > 0) {
498 c1 = &s->cells[y * last_width];
499 for(x = 0; x < w1; x++) {
500 *c++ = *c1++;
501 }
502 }
503 for(x = w1; x < s->width; x++) {
504 c->ch = ' ';
505 c->t_attrib = s->t_attrib_default;
506 c++;
507 }
508 }
509 free(s->cells);
510 s->cells = cells;
511 }
512
513 static void update_xy(TextConsole *s, int x, int y)
514 {
515 TextCell *c;
516 int y1, y2;
517
518 if (s == active_console) {
519 y1 = (s->y_base + y) % s->total_height;
520 y2 = y1 - s->y_displayed;
521 if (y2 < 0)
522 y2 += s->total_height;
523 if (y2 < s->height) {
524 c = &s->cells[y1 * s->width + x];
525 vga_putcharxy(s->ds, x, y2, c->ch,
526 &(c->t_attrib));
527 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
528 FONT_WIDTH, FONT_HEIGHT);
529 }
530 }
531 }
532
533 static void console_show_cursor(TextConsole *s, int show)
534 {
535 TextCell *c;
536 int y, y1;
537
538 if (s == active_console) {
539 y1 = (s->y_base + s->y) % s->total_height;
540 y = y1 - s->y_displayed;
541 if (y < 0)
542 y += s->total_height;
543 if (y < s->height) {
544 c = &s->cells[y1 * s->width + s->x];
545 if (show) {
546 TextAttributes t_attrib = s->t_attrib_default;
547 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
548 vga_putcharxy(s->ds, s->x, y, c->ch, &t_attrib);
549 } else {
550 vga_putcharxy(s->ds, s->x, y, c->ch,
551 &(c->t_attrib));
552 }
553 dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
554 FONT_WIDTH, FONT_HEIGHT);
555 }
556 }
557 }
558
559 static void console_refresh(TextConsole *s)
560 {
561 TextCell *c;
562 int x, y, y1;
563
564 if (s != active_console)
565 return;
566
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++;
576 }
577 if (++y1 == s->total_height)
578 y1 = 0;
579 }
580 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
581 console_show_cursor(s, 1);
582 }
583
584 static void console_scroll(int ydelta)
585 {
586 TextConsole *s;
587 int i, y1;
588
589 s = active_console;
590 if (!s || !s->text_console)
591 return;
592
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;
599 }
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;
613 }
614 }
615 console_refresh(s);
616 }
617
618 static void console_put_lf(TextConsole *s)
619 {
620 TextCell *c;
621 int x, y1;
622
623 s->y++;
624 if (s->y >= s->height) {
625 s->y = s->height - 1;
626
627 if (s->y_displayed == s->y_base) {
628 if (++s->y_displayed == s->total_height)
629 s->y_displayed = 0;
630 }
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++;
641 }
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);
651 }
652 }
653 }
654
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.
658 */
659 static void console_handle_escape(TextConsole *s)
660 {
661 int i;
662
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;
748 }
749 }
750 }
751
752 static void console_clear_xy(TextConsole *s, int x, int y)
753 {
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);
760 }
761
762 static void console_putchar(TextConsole *s, int ch)
763 {
764 TextCell *c;
765 int y1, i;
766 int x, y;
767
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));
787 }
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 - 1) {
803 break;
804 }
805 y1 = (s->y_base + s->y) % s->total_height;
806 c = &s->cells[y1 * s->width + s->x];
807 c->ch = ch;
808 c->t_attrib = s->t_attrib;
809 update_xy(s, s->x, s->y);
810 s->x++;
811 #if 0 /* line wrap disabled */
812 if (s->x >= s->width) {
813 s->x = 0;
814 console_put_lf(s);
815 }
816 #endif
817 break;
818 }
819 break;
820 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
821 if (ch == '[') {
822 for(i=0;i<MAX_ESC_PARAMS;i++)
823 s->esc_params[i] = 0;
824 s->nb_esc_params = 0;
825 s->state = TTY_STATE_CSI;
826 } else {
827 s->state = TTY_STATE_NORM;
828 }
829 break;
830 case TTY_STATE_CSI: /* handle escape sequence parameters */
831 if (ch >= '0' && ch <= '9') {
832 if (s->nb_esc_params < MAX_ESC_PARAMS) {
833 s->esc_params[s->nb_esc_params] =
834 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
835 }
836 } else {
837 s->nb_esc_params++;
838 if (ch == ';')
839 break;
840 #ifdef DEBUG_CONSOLE
841 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
842 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
843 #endif
844 s->state = TTY_STATE_NORM;
845 switch(ch) {
846 case 'A':
847 /* move cursor up */
848 if (s->esc_params[0] == 0) {
849 s->esc_params[0] = 1;
850 }
851 s->y -= s->esc_params[0];
852 if (s->y < 0) {
853 s->y = 0;
854 }
855 break;
856 case 'B':
857 /* move cursor down */
858 if (s->esc_params[0] == 0) {
859 s->esc_params[0] = 1;
860 }
861 s->y += s->esc_params[0];
862 if (s->y >= s->height) {
863 s->y = s->height - 1;
864 }
865 break;
866 case 'C':
867 /* move cursor right */
868 if (s->esc_params[0] == 0) {
869 s->esc_params[0] = 1;
870 }
871 s->x += s->esc_params[0];
872 if (s->x >= s->width) {
873 s->x = s->width - 1;
874 }
875 break;
876 case 'D':
877 /* move cursor left */
878 if (s->esc_params[0] == 0) {
879 s->esc_params[0] = 1;
880 }
881 s->x -= s->esc_params[0];
882 if (s->x < 0) {
883 s->x = 0;
884 }
885 break;
886 case 'G':
887 /* move cursor to column */
888 s->x = s->esc_params[0] - 1;
889 if (s->x < 0) {
890 s->x = 0;
891 }
892 break;
893 case 'f':
894 case 'H':
895 /* move cursor to row, column */
896 s->x = s->esc_params[1] - 1;
897 if (s->x < 0) {
898 s->x = 0;
899 }
900 s->y = s->esc_params[0] - 1;
901 if (s->y < 0) {
902 s->y = 0;
903 }
904 break;
905 case 'J':
906 switch (s->esc_params[0]) {
907 case 0:
908 /* clear to end of screen */
909 for (y = s->y; y < s->height; y++) {
910 for (x = 0; x < s->width; x++) {
911 if (y == s->y && x < s->x) {
912 continue;
913 }
914 console_clear_xy(s, x, y);
915 }
916 }
917 break;
918 case 1:
919 /* clear from beginning of screen */
920 for (y = 0; y <= s->y; y++) {
921 for (x = 0; x < s->width; x++) {
922 if (y == s->y && x > s->x) {
923 break;
924 }
925 console_clear_xy(s, x, y);
926 }
927 }
928 break;
929 case 2:
930 /* clear entire screen */
931 for (y = 0; y <= s->height; y++) {
932 for (x = 0; x < s->width; x++) {
933 console_clear_xy(s, x, y);
934 }
935 }
936 break;
937 }
938 case 'K':
939 switch (s->esc_params[0]) {
940 case 0:
941 /* clear to eol */
942 for(x = s->x; x < s->width; x++) {
943 console_clear_xy(s, x, s->y);
944 }
945 break;
946 case 1:
947 /* clear from beginning of line */
948 for (x = 0; x <= s->x; x++) {
949 console_clear_xy(s, x, s->y);
950 }
951 break;
952 case 2:
953 /* clear entire line */
954 for(x = 0; x < s->width; x++) {
955 console_clear_xy(s, x, s->y);
956 }
957 break;
958 }
959 break;
960 case 'm':
961 console_handle_escape(s);
962 break;
963 case 'n':
964 /* report cursor position */
965 /* TODO: send ESC[row;colR */
966 break;
967 case 's':
968 /* save cursor position */
969 s->x_saved = s->x;
970 s->y_saved = s->y;
971 break;
972 case 'u':
973 /* restore cursor position */
974 s->x = s->x_saved;
975 s->y = s->y_saved;
976 break;
977 default:
978 #ifdef DEBUG_CONSOLE
979 fprintf(stderr, "unhandled escape character '%c'\n", ch);
980 #endif
981 break;
982 }
983 break;
984 }
985 }
986 }
987
988 void console_select(unsigned int index)
989 {
990 TextConsole *s;
991
992 if (index >= MAX_CONSOLES)
993 return;
994 s = consoles[index];
995 if (s) {
996 active_console = s;
997 if (s->text_console) {
998 if (s->g_width != s->ds->width ||
999 s->g_height != s->ds->height) {
1000 s->g_width = s->ds->width;
1001 s->g_height = s->ds->height;
1002 text_console_resize(s);
1003 }
1004 console_refresh(s);
1005 } else {
1006 vga_hw_invalidate();
1007 }
1008 }
1009 }
1010
1011 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1012 {
1013 TextConsole *s = chr->opaque;
1014 int i;
1015
1016 console_show_cursor(s, 0);
1017 for(i = 0; i < len; i++) {
1018 console_putchar(s, buf[i]);
1019 }
1020 console_show_cursor(s, 1);
1021 return len;
1022 }
1023
1024 static void console_chr_add_read_handler(CharDriverState *chr,
1025 IOCanRWHandler *fd_can_read,
1026 IOReadHandler *fd_read, void *opaque)
1027 {
1028 TextConsole *s = chr->opaque;
1029 s->fd_can_read = fd_can_read;
1030 s->fd_read = fd_read;
1031 s->fd_opaque = opaque;
1032 }
1033
1034 static void console_send_event(CharDriverState *chr, int event)
1035 {
1036 TextConsole *s = chr->opaque;
1037 int i;
1038
1039 if (event == CHR_EVENT_FOCUS) {
1040 for(i = 0; i < nb_consoles; i++) {
1041 if (consoles[i] == s) {
1042 console_select(i);
1043 break;
1044 }
1045 }
1046 }
1047 }
1048
1049 static void kbd_send_chars(void *opaque)
1050 {
1051 TextConsole *s = opaque;
1052 int len;
1053 uint8_t buf[16];
1054
1055 len = s->fd_can_read(s->fd_opaque);
1056 if (len > s->out_fifo.count)
1057 len = s->out_fifo.count;
1058 if (len > 0) {
1059 if (len > sizeof(buf))
1060 len = sizeof(buf);
1061 qemu_fifo_read(&s->out_fifo, buf, len);
1062 s->fd_read(s->fd_opaque, buf, len);
1063 }
1064 /* characters are pending: we send them a bit later (XXX:
1065 horrible, should change char device API) */
1066 if (s->out_fifo.count > 0) {
1067 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1068 }
1069 }
1070
1071 /* called when an ascii key is pressed */
1072 void kbd_put_keysym(int keysym)
1073 {
1074 TextConsole *s;
1075 uint8_t buf[16], *q;
1076 int c;
1077
1078 s = active_console;
1079 if (!s || !s->text_console)
1080 return;
1081
1082 switch(keysym) {
1083 case QEMU_KEY_CTRL_UP:
1084 console_scroll(-1);
1085 break;
1086 case QEMU_KEY_CTRL_DOWN:
1087 console_scroll(1);
1088 break;
1089 case QEMU_KEY_CTRL_PAGEUP:
1090 console_scroll(-10);
1091 break;
1092 case QEMU_KEY_CTRL_PAGEDOWN:
1093 console_scroll(10);
1094 break;
1095 default:
1096 /* convert the QEMU keysym to VT100 key string */
1097 q = buf;
1098 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1099 *q++ = '\033';
1100 *q++ = '[';
1101 c = keysym - 0xe100;
1102 if (c >= 10)
1103 *q++ = '0' + (c / 10);
1104 *q++ = '0' + (c % 10);
1105 *q++ = '~';
1106 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1107 *q++ = '\033';
1108 *q++ = '[';
1109 *q++ = keysym & 0xff;
1110 } else {
1111 *q++ = keysym;
1112 }
1113 if (s->fd_read) {
1114 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1115 kbd_send_chars(s);
1116 }
1117 break;
1118 }
1119 }
1120
1121 static TextConsole *new_console(DisplayState *ds, int text)
1122 {
1123 TextConsole *s;
1124 int i;
1125
1126 if (nb_consoles >= MAX_CONSOLES)
1127 return NULL;
1128 s = qemu_mallocz(sizeof(TextConsole));
1129 if (!s) {
1130 return NULL;
1131 }
1132 if (!active_console || (active_console->text_console && !text))
1133 active_console = s;
1134 s->ds = ds;
1135 s->text_console = text;
1136 if (text) {
1137 consoles[nb_consoles++] = s;
1138 } else {
1139 /* HACK: Put graphical consoles before text consoles. */
1140 for (i = nb_consoles; i > 0; i--) {
1141 if (!consoles[i - 1]->text_console)
1142 break;
1143 consoles[i] = consoles[i - 1];
1144 }
1145 consoles[i] = s;
1146 }
1147 return s;
1148 }
1149
1150 TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1151 vga_hw_invalidate_ptr invalidate,
1152 vga_hw_screen_dump_ptr screen_dump,
1153 void *opaque)
1154 {
1155 TextConsole *s;
1156
1157 s = new_console(ds, 0);
1158 if (!s)
1159 return NULL;
1160 s->hw_update = update;
1161 s->hw_invalidate = invalidate;
1162 s->hw_screen_dump = screen_dump;
1163 s->hw = opaque;
1164 return s;
1165 }
1166
1167 int is_graphic_console(void)
1168 {
1169 return !active_console->text_console;
1170 }
1171
1172 CharDriverState *text_console_init(DisplayState *ds)
1173 {
1174 CharDriverState *chr;
1175 TextConsole *s;
1176 int i,j;
1177 static int color_inited;
1178
1179 chr = qemu_mallocz(sizeof(CharDriverState));
1180 if (!chr)
1181 return NULL;
1182 s = new_console(ds, 1);
1183 if (!s) {
1184 free(chr);
1185 return NULL;
1186 }
1187 chr->opaque = s;
1188 chr->chr_write = console_puts;
1189 chr->chr_add_read_handler = console_chr_add_read_handler;
1190 chr->chr_send_event = console_send_event;
1191
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);
1195
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]));
1202 }
1203 }
1204 }
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 s->g_width = s->ds->width;
1211 s->g_height = s->ds->height;
1212
1213 /* Set text attribute defaults */
1214 s->t_attrib_default.bold = 0;
1215 s->t_attrib_default.uline = 0;
1216 s->t_attrib_default.blink = 0;
1217 s->t_attrib_default.invers = 0;
1218 s->t_attrib_default.unvisible = 0;
1219 s->t_attrib_default.fgcol = COLOR_WHITE;
1220 s->t_attrib_default.bgcol = COLOR_BLACK;
1221
1222 /* set current text attributes to default */
1223 s->t_attrib = s->t_attrib_default;
1224 text_console_resize(s);
1225
1226 qemu_chr_reset(chr);
1227
1228 return chr;
1229 }