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