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