]> git.proxmox.com Git - qemu.git/blame - console.c
Improved console handling, thanks Stefan Weil.
[qemu.git] / console.c
CommitLineData
e7f0ad58
FB
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
6d6f7c28 26//#define DEBUG_CONSOLE
e7f0ad58
FB
27#define DEFAULT_BACKSCROLL 512
28#define MAX_CONSOLES 12
29
26489844
FB
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)
e7f0ad58 32
6d6f7c28
PB
33typedef 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
e7f0ad58
FB
43typedef struct TextCell {
44 uint8_t ch;
6d6f7c28 45 TextAttributes t_attrib;
e7f0ad58
FB
46} TextCell;
47
48#define MAX_ESC_PARAMS 3
49
50enum TTYState {
51 TTY_STATE_NORM,
52 TTY_STATE_ESC,
53 TTY_STATE_CSI,
54};
55
e15d7371
FB
56typedef struct QEMUFIFO {
57 uint8_t *buf;
58 int buf_size;
59 int count, wptr, rptr;
60} QEMUFIFO;
61
62int 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
85int 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
95219897
PB
107/* ??? This is mis-named.
108 It is used for both text and graphical consoles. */
e7f0ad58
FB
109struct TextConsole {
110 int text_console; /* true if text console */
111 DisplayState *ds;
95219897
PB
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
e7f0ad58
FB
118 int g_width, g_height;
119 int width;
120 int height;
121 int total_height;
122 int backscroll_height;
e7f0ad58 123 int x, y;
adb47967 124 int x_saved, y_saved;
e7f0ad58
FB
125 int y_displayed;
126 int y_base;
6d6f7c28
PB
127 TextAttributes t_attrib_default; /* default text attributes */
128 TextAttributes t_attrib; /* currently active text attributes */
e7f0ad58
FB
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 */
e15d7371 136 IOCanRWHandler *fd_can_read;
e7f0ad58
FB
137 IOReadHandler *fd_read;
138 void *fd_opaque;
e15d7371
FB
139 /* fifo for key pressed */
140 QEMUFIFO out_fifo;
141 uint8_t out_fifo_buf[16];
142 QEMUTimer *kbd_timer;
e7f0ad58
FB
143};
144
145static TextConsole *active_console;
146static TextConsole *consoles[MAX_CONSOLES];
147static int nb_consoles = 0;
148
95219897
PB
149void vga_hw_update(void)
150{
adb47967 151 if (active_console && active_console->hw_update)
95219897
PB
152 active_console->hw_update(active_console->hw);
153}
154
155void vga_hw_invalidate(void)
156{
157 if (active_console->hw_invalidate)
158 active_console->hw_invalidate(active_console->hw);
159}
160
161void 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
e7f0ad58
FB
169/* convert a RGBA color to a color index usable in graphic primitives */
170static 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
205static 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) */
241static 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
293static 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
312static const uint32_t dmask4[4] = {
313 PAT(0x00000000),
314 PAT(0x0000ffff),
315 PAT(0xffff0000),
316 PAT(0xffffffff),
317};
318
6d6f7c28
PB
319static uint32_t color_table[2][8];
320
321enum 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
332static const uint32_t color_table_rgb[2][8] = {
333 { /* dark */
26489844
FB
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 */
6d6f7c28
PB
342 },
343 { /* bright */
26489844
FB
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 */
6d6f7c28 352 }
e7f0ad58
FB
353};
354
355static 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}
6d6f7c28
PB
372#ifdef DEBUG_CONSOLE
373static 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
e7f0ad58
FB
404
405static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
6d6f7c28 406 TextAttributes *t_attrib)
e7f0ad58
FB
407{
408 uint8_t *d;
409 const uint8_t *font_ptr;
410 unsigned int font_data, linesize, xorcol, bpp;
411 int i;
6d6f7c28
PB
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 }
e7f0ad58
FB
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++;
6d6f7c28
PB
437 if (t_attrib->uline
438 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
439 font_data = 0xFFFF;
440 }
e7f0ad58
FB
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++;
6d6f7c28
PB
450 if (t_attrib->uline
451 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
452 font_data = 0xFFFF;
453 }
e7f0ad58
FB
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++;
6d6f7c28
PB
464 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
465 font_data = 0xFFFF;
466 }
e7f0ad58
FB
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
481static 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 = ' ';
6d6f7c28 505 c->t_attrib = s->t_attrib_default;
e7f0ad58
FB
506 c++;
507 }
508 }
509 free(s->cells);
510 s->cells = cells;
511}
512
513static 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,
6d6f7c28 526 &(c->t_attrib));
e7f0ad58
FB
527 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
528 FONT_WIDTH, FONT_HEIGHT);
529 }
530 }
531}
532
533static 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) {
6d6f7c28
PB
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);
e7f0ad58
FB
549 } else {
550 vga_putcharxy(s->ds, s->x, y, c->ch,
6d6f7c28 551 &(c->t_attrib));
e7f0ad58
FB
552 }
553 dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
554 FONT_WIDTH, FONT_HEIGHT);
555 }
556 }
557}
558
559static 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,
6d6f7c28 568 color_table[0][COLOR_BLACK]);
e7f0ad58
FB
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,
6d6f7c28 574 &(c->t_attrib));
e7f0ad58
FB
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
584static 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
618static void console_put_lf(TextConsole *s)
619{
620 TextCell *c;
621 int x, y1;
622
e7f0ad58
FB
623 s->y++;
624 if (s->y >= s->height) {
625 s->y = s->height - 1;
6d6f7c28 626
e7f0ad58
FB
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 = ' ';
6d6f7c28 639 c->t_attrib = s->t_attrib_default;
e7f0ad58
FB
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,
6d6f7c28 648 color_table[0][s->t_attrib_default.bgcol]);
e7f0ad58
FB
649 dpy_update(s->ds, 0, 0,
650 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
651 }
652 }
653}
654
6d6f7c28
PB
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 */
659static void console_handle_escape(TextConsole *s)
660{
661 int i;
662
6d6f7c28
PB
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
adb47967
TS
752static 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
e7f0ad58
FB
762static void console_putchar(TextConsole *s, int ch)
763{
764 TextCell *c;
adb47967
TS
765 int y1, i;
766 int x, y;
e7f0ad58
FB
767
768 switch(s->state) {
769 case TTY_STATE_NORM:
770 switch(ch) {
6d6f7c28 771 case '\r': /* carriage return */
e7f0ad58
FB
772 s->x = 0;
773 break;
6d6f7c28 774 case '\n': /* newline */
e7f0ad58
FB
775 console_put_lf(s);
776 break;
6d6f7c28 777 case '\b': /* backspace */
e15d7371
FB
778 if (s->x > 0)
779 s->x--;
6d6f7c28
PB
780 break;
781 case '\t': /* tabspace */
782 if (s->x + (8 - (s->x % 8)) > s->width) {
bd468840 783 s->x = 0;
6d6f7c28
PB
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;
adb47967
TS
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;
6d6f7c28 798 case 27: /* esc (introducing an escape sequence) */
e7f0ad58
FB
799 s->state = TTY_STATE_ESC;
800 break;
801 default:
adb47967
TS
802 if (s->x >= s->width - 1) {
803 break;
804 }
e7f0ad58
FB
805 y1 = (s->y_base + s->y) % s->total_height;
806 c = &s->cells[y1 * s->width + s->x];
807 c->ch = ch;
6d6f7c28 808 c->t_attrib = s->t_attrib;
e7f0ad58
FB
809 update_xy(s, s->x, s->y);
810 s->x++;
adb47967 811#if 0 /* line wrap disabled */
bd468840
FB
812 if (s->x >= s->width) {
813 s->x = 0;
e7f0ad58 814 console_put_lf(s);
bd468840 815 }
adb47967 816#endif
e7f0ad58
FB
817 break;
818 }
819 break;
6d6f7c28 820 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
e7f0ad58
FB
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;
6d6f7c28 830 case TTY_STATE_CSI: /* handle escape sequence parameters */
e7f0ad58
FB
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;
adb47967
TS
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
e7f0ad58
FB
844 s->state = TTY_STATE_NORM;
845 switch(ch) {
adb47967
TS
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 }
e7f0ad58
FB
865 break;
866 case 'C':
adb47967
TS
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 }
e7f0ad58 875 break;
adb47967
TS
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 }
e7f0ad58 938 case 'K':
adb47967
TS
939 switch (s->esc_params[0]) {
940 case 0:
e7f0ad58 941 /* clear to eol */
e7f0ad58 942 for(x = s->x; x < s->width; x++) {
adb47967 943 console_clear_xy(s, x, s->y);
e7f0ad58
FB
944 }
945 break;
adb47967
TS
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 }
e7f0ad58
FB
957 break;
958 }
adb47967
TS
959 break;
960 case 'm':
6d6f7c28 961 console_handle_escape(s);
e7f0ad58 962 break;
adb47967
TS
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;
e7f0ad58
FB
984 }
985 }
986}
987
988void console_select(unsigned int index)
989{
990 TextConsole *s;
6d6f7c28 991
e7f0ad58
FB
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 ||
8e3a9fd2 999 s->g_height != s->ds->height) {
6d6f7c28
PB
1000 s->g_width = s->ds->width;
1001 s->g_height = s->ds->height;
e7f0ad58 1002 text_console_resize(s);
95219897 1003 }
e7f0ad58 1004 console_refresh(s);
95219897
PB
1005 } else {
1006 vga_hw_invalidate();
e7f0ad58
FB
1007 }
1008 }
1009}
1010
1011static 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
1024static 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;
e15d7371 1029 s->fd_can_read = fd_can_read;
e7f0ad58
FB
1030 s->fd_read = fd_read;
1031 s->fd_opaque = opaque;
1032}
1033
6fcfafb7
FB
1034static 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
e15d7371
FB
1049static 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
e7f0ad58
FB
1071/* called when an ascii key is pressed */
1072void 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:
e15d7371
FB
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 {
e7f0ad58 1111 *q++ = keysym;
e15d7371
FB
1112 }
1113 if (s->fd_read) {
1114 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1115 kbd_send_chars(s);
e7f0ad58
FB
1116 }
1117 break;
1118 }
1119}
1120
95219897 1121static TextConsole *new_console(DisplayState *ds, int text)
e7f0ad58
FB
1122{
1123 TextConsole *s;
95219897 1124 int i;
e7f0ad58
FB
1125
1126 if (nb_consoles >= MAX_CONSOLES)
1127 return NULL;
1128 s = qemu_mallocz(sizeof(TextConsole));
1129 if (!s) {
1130 return NULL;
1131 }
95219897 1132 if (!active_console || (active_console->text_console && !text))
e7f0ad58
FB
1133 active_console = s;
1134 s->ds = ds;
95219897
PB
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
1150TextConsole *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;
e7f0ad58
FB
1164 return s;
1165}
1166
95219897 1167int is_graphic_console(void)
e7f0ad58 1168{
95219897 1169 return !active_console->text_console;
e7f0ad58
FB
1170}
1171
1172CharDriverState *text_console_init(DisplayState *ds)
1173{
1174 CharDriverState *chr;
1175 TextConsole *s;
6d6f7c28 1176 int i,j;
e7f0ad58 1177 static int color_inited;
6d6f7c28 1178
e7f0ad58
FB
1179 chr = qemu_mallocz(sizeof(CharDriverState));
1180 if (!chr)
1181 return NULL;
95219897 1182 s = new_console(ds, 1);
e7f0ad58
FB
1183 if (!s) {
1184 free(chr);
1185 return NULL;
1186 }
e7f0ad58
FB
1187 chr->opaque = s;
1188 chr->chr_write = console_puts;
1189 chr->chr_add_read_handler = console_chr_add_read_handler;
6fcfafb7
FB
1190 chr->chr_send_event = console_send_event;
1191
e15d7371
FB
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
e7f0ad58
FB
1196 if (!color_inited) {
1197 color_inited = 1;
6d6f7c28
PB
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 }
e7f0ad58
FB
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;
e7f0ad58
FB
1210 s->g_width = s->ds->width;
1211 s->g_height = s->ds->height;
6d6f7c28
PB
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;
e7f0ad58
FB
1224 text_console_resize(s);
1225
86e94dea
TS
1226 qemu_chr_reset(chr);
1227
e7f0ad58
FB
1228 return chr;
1229}