]> git.proxmox.com Git - qemu.git/blob - console.c
Use the actual executable names in documentation, based on a patch by
[qemu.git] / console.c
1 /*
2 * QEMU graphical console
3 *
4 * Copyright (c) 2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include "vl.h"
25
26 //#define DEBUG_CONSOLE
27 #define DEFAULT_BACKSCROLL 512
28 #define MAX_CONSOLES 12
29
30 #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
31 #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
32
33 typedef struct TextAttributes {
34 uint8_t fgcol:4;
35 uint8_t bgcol:4;
36 uint8_t bold:1;
37 uint8_t uline:1;
38 uint8_t blink:1;
39 uint8_t invers:1;
40 uint8_t unvisible:1;
41 } TextAttributes;
42
43 typedef struct TextCell {
44 uint8_t ch;
45 TextAttributes t_attrib;
46 } TextCell;
47
48 #define MAX_ESC_PARAMS 3
49
50 enum TTYState {
51 TTY_STATE_NORM,
52 TTY_STATE_ESC,
53 TTY_STATE_CSI,
54 };
55
56 typedef struct QEMUFIFO {
57 uint8_t *buf;
58 int buf_size;
59 int count, wptr, rptr;
60 } QEMUFIFO;
61
62 int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
63 {
64 int l, len;
65
66 l = f->buf_size - f->count;
67 if (len1 > l)
68 len1 = l;
69 len = len1;
70 while (len > 0) {
71 l = f->buf_size - f->wptr;
72 if (l > len)
73 l = len;
74 memcpy(f->buf + f->wptr, buf, l);
75 f->wptr += l;
76 if (f->wptr >= f->buf_size)
77 f->wptr = 0;
78 buf += l;
79 len -= l;
80 }
81 f->count += len1;
82 return len1;
83 }
84
85 int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
86 {
87 int l, len;
88
89 if (len1 > f->count)
90 len1 = f->count;
91 len = len1;
92 while (len > 0) {
93 l = f->buf_size - f->rptr;
94 if (l > len)
95 l = len;
96 memcpy(buf, f->buf + f->rptr, l);
97 f->rptr += l;
98 if (f->rptr >= f->buf_size)
99 f->rptr = 0;
100 buf += l;
101 len -= l;
102 }
103 f->count -= len1;
104 return len1;
105 }
106
107 /* ??? This is mis-named.
108 It is used for both text and graphical consoles. */
109 struct TextConsole {
110 int text_console; /* true if text console */
111 DisplayState *ds;
112 /* Graphic console state. */
113 vga_hw_update_ptr hw_update;
114 vga_hw_invalidate_ptr hw_invalidate;
115 vga_hw_screen_dump_ptr hw_screen_dump;
116 void *hw;
117
118 int g_width, g_height;
119 int width;
120 int height;
121 int total_height;
122 int backscroll_height;
123 int x, y;
124 int x_saved, y_saved;
125 int y_displayed;
126 int y_base;
127 TextAttributes t_attrib_default; /* default text attributes */
128 TextAttributes t_attrib; /* currently active text attributes */
129 TextCell *cells;
130
131 enum TTYState state;
132 int esc_params[MAX_ESC_PARAMS];
133 int nb_esc_params;
134
135 CharDriverState *chr;
136 /* fifo for key pressed */
137 QEMUFIFO out_fifo;
138 uint8_t out_fifo_buf[16];
139 QEMUTimer *kbd_timer;
140 };
141
142 static TextConsole *active_console;
143 static TextConsole *consoles[MAX_CONSOLES];
144 static int nb_consoles = 0;
145
146 void vga_hw_update(void)
147 {
148 if (active_console && active_console->hw_update)
149 active_console->hw_update(active_console->hw);
150 }
151
152 void vga_hw_invalidate(void)
153 {
154 if (active_console->hw_invalidate)
155 active_console->hw_invalidate(active_console->hw);
156 }
157
158 void 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
166 /* convert a RGBA color to a color index usable in graphic primitives */
167 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
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
202 static 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) */
238 static 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
290 static const uint32_t dmask16[16] = {
291 PAT(0x00000000),
292 PAT(0x000000ff),
293 PAT(0x0000ff00),
294 PAT(0x0000ffff),
295 PAT(0x00ff0000),
296 PAT(0x00ff00ff),
297 PAT(0x00ffff00),
298 PAT(0x00ffffff),
299 PAT(0xff000000),
300 PAT(0xff0000ff),
301 PAT(0xff00ff00),
302 PAT(0xff00ffff),
303 PAT(0xffff0000),
304 PAT(0xffff00ff),
305 PAT(0xffffff00),
306 PAT(0xffffffff),
307 };
308
309 static const uint32_t dmask4[4] = {
310 PAT(0x00000000),
311 PAT(0x0000ffff),
312 PAT(0xffff0000),
313 PAT(0xffffffff),
314 };
315
316 static uint32_t color_table[2][8];
317
318 enum color_names {
319 COLOR_BLACK = 0,
320 COLOR_RED = 1,
321 COLOR_GREEN = 2,
322 COLOR_YELLOW = 3,
323 COLOR_BLUE = 4,
324 COLOR_MAGENTA = 5,
325 COLOR_CYAN = 6,
326 COLOR_WHITE = 7
327 };
328
329 static const uint32_t color_table_rgb[2][8] = {
330 { /* dark */
331 QEMU_RGB(0x00, 0x00, 0x00), /* black */
332 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
333 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
334 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
335 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
336 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
337 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
338 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
339 },
340 { /* bright */
341 QEMU_RGB(0x00, 0x00, 0x00), /* black */
342 QEMU_RGB(0xff, 0x00, 0x00), /* red */
343 QEMU_RGB(0x00, 0xff, 0x00), /* green */
344 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
345 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
346 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
347 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
348 QEMU_RGB(0xff, 0xff, 0xff), /* white */
349 }
350 };
351
352 static 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 }
369 #ifdef DEBUG_CONSOLE
370 static 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
401
402 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
403 TextAttributes *t_attrib)
404 {
405 uint8_t *d;
406 const uint8_t *font_ptr;
407 unsigned int font_data, linesize, xorcol, bpp;
408 int i;
409 unsigned int fgcol, bgcol;
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 }
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++;
434 if (t_attrib->uline
435 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
436 font_data = 0xFFFF;
437 }
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++;
447 if (t_attrib->uline
448 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
449 font_data = 0xFFFF;
450 }
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++;
461 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
462 font_data = 0xFFFF;
463 }
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
478 static 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 = ' ';
502 c->t_attrib = s->t_attrib_default;
503 c++;
504 }
505 }
506 free(s->cells);
507 s->cells = cells;
508 }
509
510 static 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,
523 &(c->t_attrib));
524 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
525 FONT_WIDTH, FONT_HEIGHT);
526 }
527 }
528 }
529
530 static 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) {
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);
546 } else {
547 vga_putcharxy(s->ds, s->x, y, c->ch,
548 &(c->t_attrib));
549 }
550 dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
551 FONT_WIDTH, FONT_HEIGHT);
552 }
553 }
554 }
555
556 static 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,
565 color_table[0][COLOR_BLACK]);
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,
571 &(c->t_attrib));
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
581 static 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
615 static void console_put_lf(TextConsole *s)
616 {
617 TextCell *c;
618 int x, y1;
619
620 s->y++;
621 if (s->y >= s->height) {
622 s->y = s->height - 1;
623
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 = ' ';
636 c->t_attrib = s->t_attrib_default;
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,
645 color_table[0][s->t_attrib_default.bgcol]);
646 dpy_update(s->ds, 0, 0,
647 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
648 }
649 }
650 }
651
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 */
656 static void console_handle_escape(TextConsole *s)
657 {
658 int i;
659
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
749 static 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
759 static void console_putchar(TextConsole *s, int ch)
760 {
761 TextCell *c;
762 int y1, i;
763 int x, y;
764
765 switch(s->state) {
766 case TTY_STATE_NORM:
767 switch(ch) {
768 case '\r': /* carriage return */
769 s->x = 0;
770 break;
771 case '\n': /* newline */
772 console_put_lf(s);
773 break;
774 case '\b': /* backspace */
775 if (s->x > 0)
776 s->x--;
777 break;
778 case '\t': /* tabspace */
779 if (s->x + (8 - (s->x % 8)) > s->width) {
780 s->x = 0;
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;
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;
795 case 27: /* esc (introducing an escape sequence) */
796 s->state = TTY_STATE_ESC;
797 break;
798 default:
799 if (s->x >= s->width - 1) {
800 break;
801 }
802 y1 = (s->y_base + s->y) % s->total_height;
803 c = &s->cells[y1 * s->width + s->x];
804 c->ch = ch;
805 c->t_attrib = s->t_attrib;
806 update_xy(s, s->x, s->y);
807 s->x++;
808 #if 0 /* line wrap disabled */
809 if (s->x >= s->width) {
810 s->x = 0;
811 console_put_lf(s);
812 }
813 #endif
814 break;
815 }
816 break;
817 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
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;
827 case TTY_STATE_CSI: /* handle escape sequence parameters */
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;
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
841 s->state = TTY_STATE_NORM;
842 switch(ch) {
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 }
862 break;
863 case 'C':
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 }
872 break;
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 }
935 case 'K':
936 switch (s->esc_params[0]) {
937 case 0:
938 /* clear to eol */
939 for(x = s->x; x < s->width; x++) {
940 console_clear_xy(s, x, s->y);
941 }
942 break;
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 }
954 break;
955 }
956 break;
957 case 'm':
958 console_handle_escape(s);
959 break;
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;
981 }
982 }
983 }
984
985 void console_select(unsigned int index)
986 {
987 TextConsole *s;
988
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 ||
996 s->g_height != s->ds->height) {
997 s->g_width = s->ds->width;
998 s->g_height = s->ds->height;
999 text_console_resize(s);
1000 }
1001 console_refresh(s);
1002 } else {
1003 vga_hw_invalidate();
1004 }
1005 }
1006 }
1007
1008 static 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
1021 static 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
1036 static void kbd_send_chars(void *opaque)
1037 {
1038 TextConsole *s = opaque;
1039 int len;
1040 uint8_t buf[16];
1041
1042 len = qemu_chr_can_read(s->chr);
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);
1049 qemu_chr_read(s->chr, buf, len);
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
1058 /* called when an ascii key is pressed */
1059 void 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:
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 {
1098 *q++ = keysym;
1099 }
1100 if (s->chr->chr_read) {
1101 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1102 kbd_send_chars(s);
1103 }
1104 break;
1105 }
1106 }
1107
1108 static TextConsole *new_console(DisplayState *ds, int text)
1109 {
1110 TextConsole *s;
1111 int i;
1112
1113 if (nb_consoles >= MAX_CONSOLES)
1114 return NULL;
1115 s = qemu_mallocz(sizeof(TextConsole));
1116 if (!s) {
1117 return NULL;
1118 }
1119 if (!active_console || (active_console->text_console && !text))
1120 active_console = s;
1121 s->ds = ds;
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
1137 TextConsole *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;
1151 return s;
1152 }
1153
1154 int is_graphic_console(void)
1155 {
1156 return !active_console->text_console;
1157 }
1158
1159 CharDriverState *text_console_init(DisplayState *ds)
1160 {
1161 CharDriverState *chr;
1162 TextConsole *s;
1163 int i,j;
1164 static int color_inited;
1165
1166 chr = qemu_mallocz(sizeof(CharDriverState));
1167 if (!chr)
1168 return NULL;
1169 s = new_console(ds, 1);
1170 if (!s) {
1171 free(chr);
1172 return NULL;
1173 }
1174 chr->opaque = s;
1175 chr->chr_write = console_puts;
1176 chr->chr_send_event = console_send_event;
1177
1178 s->chr = chr;
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
1183 if (!color_inited) {
1184 color_inited = 1;
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 }
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;
1197 s->g_width = s->ds->width;
1198 s->g_height = s->ds->height;
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;
1211 text_console_resize(s);
1212
1213 qemu_chr_reset(chr);
1214
1215 return chr;
1216 }