]> git.proxmox.com Git - mirror_qemu.git/blob - console.c
Allow multiple graphics devices.
[mirror_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 RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
31 #define RGB(r, g, b) 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 /* ??? This is mis-named.
57 It is used for both text and graphical consoles. */
58 struct TextConsole {
59 int text_console; /* true if text console */
60 DisplayState *ds;
61 /* Graphic console state. */
62 vga_hw_update_ptr hw_update;
63 vga_hw_invalidate_ptr hw_invalidate;
64 vga_hw_screen_dump_ptr hw_screen_dump;
65 void *hw;
66
67 int g_width, g_height;
68 int width;
69 int height;
70 int total_height;
71 int backscroll_height;
72 int x, y;
73 int y_displayed;
74 int y_base;
75 TextAttributes t_attrib_default; /* default text attributes */
76 TextAttributes t_attrib; /* currently active text attributes */
77 TextCell *cells;
78
79 enum TTYState state;
80 int esc_params[MAX_ESC_PARAMS];
81 int nb_esc_params;
82
83 /* kbd read handler */
84 IOReadHandler *fd_read;
85 void *fd_opaque;
86 };
87
88 static TextConsole *active_console;
89 static TextConsole *consoles[MAX_CONSOLES];
90 static int nb_consoles = 0;
91
92 void vga_hw_update(void)
93 {
94 if (active_console->hw_update)
95 active_console->hw_update(active_console->hw);
96 }
97
98 void vga_hw_invalidate(void)
99 {
100 if (active_console->hw_invalidate)
101 active_console->hw_invalidate(active_console->hw);
102 }
103
104 void vga_hw_screen_dump(const char *filename)
105 {
106 /* There is currently no was of specifying which screen we want to dump,
107 so always dump the dirst one. */
108 if (consoles[0]->hw_screen_dump)
109 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
110 }
111
112 /* convert a RGBA color to a color index usable in graphic primitives */
113 static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
114 {
115 unsigned int r, g, b, color;
116
117 switch(ds->depth) {
118 #if 0
119 case 8:
120 r = (rgba >> 16) & 0xff;
121 g = (rgba >> 8) & 0xff;
122 b = (rgba) & 0xff;
123 color = (rgb_to_index[r] * 6 * 6) +
124 (rgb_to_index[g] * 6) +
125 (rgb_to_index[b]);
126 break;
127 #endif
128 case 15:
129 r = (rgba >> 16) & 0xff;
130 g = (rgba >> 8) & 0xff;
131 b = (rgba) & 0xff;
132 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
133 break;
134 case 16:
135 r = (rgba >> 16) & 0xff;
136 g = (rgba >> 8) & 0xff;
137 b = (rgba) & 0xff;
138 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
139 break;
140 case 32:
141 default:
142 color = rgba;
143 break;
144 }
145 return color;
146 }
147
148 static void vga_fill_rect (DisplayState *ds,
149 int posx, int posy, int width, int height, uint32_t color)
150 {
151 uint8_t *d, *d1;
152 int x, y, bpp;
153
154 bpp = (ds->depth + 7) >> 3;
155 d1 = ds->data +
156 ds->linesize * posy + bpp * posx;
157 for (y = 0; y < height; y++) {
158 d = d1;
159 switch(bpp) {
160 case 1:
161 for (x = 0; x < width; x++) {
162 *((uint8_t *)d) = color;
163 d++;
164 }
165 break;
166 case 2:
167 for (x = 0; x < width; x++) {
168 *((uint16_t *)d) = color;
169 d += 2;
170 }
171 break;
172 case 4:
173 for (x = 0; x < width; x++) {
174 *((uint32_t *)d) = color;
175 d += 4;
176 }
177 break;
178 }
179 d1 += ds->linesize;
180 }
181 }
182
183 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
184 static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
185 {
186 const uint8_t *s;
187 uint8_t *d;
188 int wb, y, bpp;
189
190 bpp = (ds->depth + 7) >> 3;
191 wb = w * bpp;
192 if (yd <= ys) {
193 s = ds->data +
194 ds->linesize * ys + bpp * xs;
195 d = ds->data +
196 ds->linesize * yd + bpp * xd;
197 for (y = 0; y < h; y++) {
198 memmove(d, s, wb);
199 d += ds->linesize;
200 s += ds->linesize;
201 }
202 } else {
203 s = ds->data +
204 ds->linesize * (ys + h - 1) + bpp * xs;
205 d = ds->data +
206 ds->linesize * (yd + h - 1) + bpp * xd;
207 for (y = 0; y < h; y++) {
208 memmove(d, s, wb);
209 d -= ds->linesize;
210 s -= ds->linesize;
211 }
212 }
213 }
214
215 /***********************************************************/
216 /* basic char display */
217
218 #define FONT_HEIGHT 16
219 #define FONT_WIDTH 8
220
221 #include "vgafont.h"
222
223 #define cbswap_32(__x) \
224 ((uint32_t)( \
225 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
226 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
227 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
228 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
229
230 #ifdef WORDS_BIGENDIAN
231 #define PAT(x) x
232 #else
233 #define PAT(x) cbswap_32(x)
234 #endif
235
236 static const uint32_t dmask16[16] = {
237 PAT(0x00000000),
238 PAT(0x000000ff),
239 PAT(0x0000ff00),
240 PAT(0x0000ffff),
241 PAT(0x00ff0000),
242 PAT(0x00ff00ff),
243 PAT(0x00ffff00),
244 PAT(0x00ffffff),
245 PAT(0xff000000),
246 PAT(0xff0000ff),
247 PAT(0xff00ff00),
248 PAT(0xff00ffff),
249 PAT(0xffff0000),
250 PAT(0xffff00ff),
251 PAT(0xffffff00),
252 PAT(0xffffffff),
253 };
254
255 static const uint32_t dmask4[4] = {
256 PAT(0x00000000),
257 PAT(0x0000ffff),
258 PAT(0xffff0000),
259 PAT(0xffffffff),
260 };
261
262 static uint32_t color_table[2][8];
263
264 enum color_names {
265 COLOR_BLACK = 0,
266 COLOR_RED = 1,
267 COLOR_GREEN = 2,
268 COLOR_YELLOW = 3,
269 COLOR_BLUE = 4,
270 COLOR_MAGENTA = 5,
271 COLOR_CYAN = 6,
272 COLOR_WHITE = 7
273 };
274
275 static const uint32_t color_table_rgb[2][8] = {
276 { /* dark */
277 RGB(0x00, 0x00, 0x00), /* black */
278 RGB(0xaa, 0x00, 0x00), /* red */
279 RGB(0x00, 0xaa, 0x00), /* green */
280 RGB(0xaa, 0xaa, 0x00), /* yellow */
281 RGB(0x00, 0x00, 0xaa), /* blue */
282 RGB(0xaa, 0x00, 0xaa), /* magenta */
283 RGB(0x00, 0xaa, 0xaa), /* cyan */
284 RGB(0xaa, 0xaa, 0xaa), /* white */
285 },
286 { /* bright */
287 RGB(0x00, 0x00, 0x00), /* black */
288 RGB(0xff, 0x00, 0x00), /* red */
289 RGB(0x00, 0xff, 0x00), /* green */
290 RGB(0xff, 0xff, 0x00), /* yellow */
291 RGB(0x00, 0x00, 0xff), /* blue */
292 RGB(0xff, 0x00, 0xff), /* magenta */
293 RGB(0x00, 0xff, 0xff), /* cyan */
294 RGB(0xff, 0xff, 0xff), /* white */
295 }
296 };
297
298 static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
299 {
300 switch(ds->depth) {
301 case 8:
302 col |= col << 8;
303 col |= col << 16;
304 break;
305 case 15:
306 case 16:
307 col |= col << 16;
308 break;
309 default:
310 break;
311 }
312
313 return col;
314 }
315 #ifdef DEBUG_CONSOLE
316 static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
317 {
318 if (t_attrib->bold) {
319 printf("b");
320 } else {
321 printf(" ");
322 }
323 if (t_attrib->uline) {
324 printf("u");
325 } else {
326 printf(" ");
327 }
328 if (t_attrib->blink) {
329 printf("l");
330 } else {
331 printf(" ");
332 }
333 if (t_attrib->invers) {
334 printf("i");
335 } else {
336 printf(" ");
337 }
338 if (t_attrib->unvisible) {
339 printf("n");
340 } else {
341 printf(" ");
342 }
343
344 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
345 }
346 #endif
347
348 static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
349 TextAttributes *t_attrib)
350 {
351 uint8_t *d;
352 const uint8_t *font_ptr;
353 unsigned int font_data, linesize, xorcol, bpp;
354 int i;
355 unsigned int fgcol, bgcol;
356
357 #ifdef DEBUG_CONSOLE
358 printf("x: %2i y: %2i", x, y);
359 console_print_text_attributes(t_attrib, ch);
360 #endif
361
362 if (t_attrib->invers) {
363 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
364 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
365 } else {
366 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
367 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
368 }
369
370 bpp = (ds->depth + 7) >> 3;
371 d = ds->data +
372 ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
373 linesize = ds->linesize;
374 font_ptr = vgafont16 + FONT_HEIGHT * ch;
375 xorcol = bgcol ^ fgcol;
376 switch(ds->depth) {
377 case 8:
378 for(i = 0; i < FONT_HEIGHT; i++) {
379 font_data = *font_ptr++;
380 if (t_attrib->uline
381 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
382 font_data = 0xFFFF;
383 }
384 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
385 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
386 d += linesize;
387 }
388 break;
389 case 16:
390 case 15:
391 for(i = 0; i < FONT_HEIGHT; i++) {
392 font_data = *font_ptr++;
393 if (t_attrib->uline
394 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
395 font_data = 0xFFFF;
396 }
397 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
398 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
399 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
400 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
401 d += linesize;
402 }
403 break;
404 case 32:
405 for(i = 0; i < FONT_HEIGHT; i++) {
406 font_data = *font_ptr++;
407 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
408 font_data = 0xFFFF;
409 }
410 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
411 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
412 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
413 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
414 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
415 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
416 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
417 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
418 d += linesize;
419 }
420 break;
421 }
422 }
423
424 static void text_console_resize(TextConsole *s)
425 {
426 TextCell *cells, *c, *c1;
427 int w1, x, y, last_width;
428
429 last_width = s->width;
430 s->width = s->g_width / FONT_WIDTH;
431 s->height = s->g_height / FONT_HEIGHT;
432
433 w1 = last_width;
434 if (s->width < w1)
435 w1 = s->width;
436
437 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
438 for(y = 0; y < s->total_height; y++) {
439 c = &cells[y * s->width];
440 if (w1 > 0) {
441 c1 = &s->cells[y * last_width];
442 for(x = 0; x < w1; x++) {
443 *c++ = *c1++;
444 }
445 }
446 for(x = w1; x < s->width; x++) {
447 c->ch = ' ';
448 c->t_attrib = s->t_attrib_default;
449 c++;
450 }
451 }
452 free(s->cells);
453 s->cells = cells;
454 }
455
456 static void update_xy(TextConsole *s, int x, int y)
457 {
458 TextCell *c;
459 int y1, y2;
460
461 if (s == active_console) {
462 y1 = (s->y_base + y) % s->total_height;
463 y2 = y1 - s->y_displayed;
464 if (y2 < 0)
465 y2 += s->total_height;
466 if (y2 < s->height) {
467 c = &s->cells[y1 * s->width + x];
468 vga_putcharxy(s->ds, x, y2, c->ch,
469 &(c->t_attrib));
470 dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
471 FONT_WIDTH, FONT_HEIGHT);
472 }
473 }
474 }
475
476 static void console_show_cursor(TextConsole *s, int show)
477 {
478 TextCell *c;
479 int y, y1;
480
481 if (s == active_console) {
482 y1 = (s->y_base + s->y) % s->total_height;
483 y = y1 - s->y_displayed;
484 if (y < 0)
485 y += s->total_height;
486 if (y < s->height) {
487 c = &s->cells[y1 * s->width + s->x];
488 if (show) {
489 TextAttributes t_attrib = s->t_attrib_default;
490 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
491 vga_putcharxy(s->ds, s->x, y, c->ch, &t_attrib);
492 } else {
493 vga_putcharxy(s->ds, s->x, y, c->ch,
494 &(c->t_attrib));
495 }
496 dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT,
497 FONT_WIDTH, FONT_HEIGHT);
498 }
499 }
500 }
501
502 static void console_refresh(TextConsole *s)
503 {
504 TextCell *c;
505 int x, y, y1;
506
507 if (s != active_console)
508 return;
509
510 vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
511 color_table[0][COLOR_BLACK]);
512 y1 = s->y_displayed;
513 for(y = 0; y < s->height; y++) {
514 c = s->cells + y1 * s->width;
515 for(x = 0; x < s->width; x++) {
516 vga_putcharxy(s->ds, x, y, c->ch,
517 &(c->t_attrib));
518 c++;
519 }
520 if (++y1 == s->total_height)
521 y1 = 0;
522 }
523 dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
524 console_show_cursor(s, 1);
525 }
526
527 static void console_scroll(int ydelta)
528 {
529 TextConsole *s;
530 int i, y1;
531
532 s = active_console;
533 if (!s || !s->text_console)
534 return;
535
536 if (ydelta > 0) {
537 for(i = 0; i < ydelta; i++) {
538 if (s->y_displayed == s->y_base)
539 break;
540 if (++s->y_displayed == s->total_height)
541 s->y_displayed = 0;
542 }
543 } else {
544 ydelta = -ydelta;
545 i = s->backscroll_height;
546 if (i > s->total_height - s->height)
547 i = s->total_height - s->height;
548 y1 = s->y_base - i;
549 if (y1 < 0)
550 y1 += s->total_height;
551 for(i = 0; i < ydelta; i++) {
552 if (s->y_displayed == y1)
553 break;
554 if (--s->y_displayed < 0)
555 s->y_displayed = s->total_height - 1;
556 }
557 }
558 console_refresh(s);
559 }
560
561 static void console_put_lf(TextConsole *s)
562 {
563 TextCell *c;
564 int x, y1;
565
566 s->x = 0;
567 s->y++;
568 if (s->y >= s->height) {
569 s->y = s->height - 1;
570
571 if (s->y_displayed == s->y_base) {
572 if (++s->y_displayed == s->total_height)
573 s->y_displayed = 0;
574 }
575 if (++s->y_base == s->total_height)
576 s->y_base = 0;
577 if (s->backscroll_height < s->total_height)
578 s->backscroll_height++;
579 y1 = (s->y_base + s->height - 1) % s->total_height;
580 c = &s->cells[y1 * s->width];
581 for(x = 0; x < s->width; x++) {
582 c->ch = ' ';
583 c->t_attrib = s->t_attrib_default;
584 c++;
585 }
586 if (s == active_console && s->y_displayed == s->y_base) {
587 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
588 s->width * FONT_WIDTH,
589 (s->height - 1) * FONT_HEIGHT);
590 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
591 s->width * FONT_WIDTH, FONT_HEIGHT,
592 color_table[0][s->t_attrib_default.bgcol]);
593 dpy_update(s->ds, 0, 0,
594 s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
595 }
596 }
597 }
598
599 /* Set console attributes depending on the current escape codes.
600 * NOTE: I know this code is not very efficient (checking every color for it
601 * self) but it is more readable and better maintainable.
602 */
603 static void console_handle_escape(TextConsole *s)
604 {
605 int i;
606
607 if (s->nb_esc_params == 0) { /* ESC[m sets all attributes to default */
608 s->t_attrib = s->t_attrib_default;
609 return;
610 }
611 for (i=0; i<s->nb_esc_params; i++) {
612 switch (s->esc_params[i]) {
613 case 0: /* reset all console attributes to default */
614 s->t_attrib = s->t_attrib_default;
615 break;
616 case 1:
617 s->t_attrib.bold = 1;
618 break;
619 case 4:
620 s->t_attrib.uline = 1;
621 break;
622 case 5:
623 s->t_attrib.blink = 1;
624 break;
625 case 7:
626 s->t_attrib.invers = 1;
627 break;
628 case 8:
629 s->t_attrib.unvisible = 1;
630 break;
631 case 22:
632 s->t_attrib.bold = 0;
633 break;
634 case 24:
635 s->t_attrib.uline = 0;
636 break;
637 case 25:
638 s->t_attrib.blink = 0;
639 break;
640 case 27:
641 s->t_attrib.invers = 0;
642 break;
643 case 28:
644 s->t_attrib.unvisible = 0;
645 break;
646 /* set foreground color */
647 case 30:
648 s->t_attrib.fgcol=COLOR_BLACK;
649 break;
650 case 31:
651 s->t_attrib.fgcol=COLOR_RED;
652 break;
653 case 32:
654 s->t_attrib.fgcol=COLOR_GREEN;
655 break;
656 case 33:
657 s->t_attrib.fgcol=COLOR_YELLOW;
658 break;
659 case 34:
660 s->t_attrib.fgcol=COLOR_BLUE;
661 break;
662 case 35:
663 s->t_attrib.fgcol=COLOR_MAGENTA;
664 break;
665 case 36:
666 s->t_attrib.fgcol=COLOR_CYAN;
667 break;
668 case 37:
669 s->t_attrib.fgcol=COLOR_WHITE;
670 break;
671 /* set background color */
672 case 40:
673 s->t_attrib.bgcol=COLOR_BLACK;
674 break;
675 case 41:
676 s->t_attrib.bgcol=COLOR_RED;
677 break;
678 case 42:
679 s->t_attrib.bgcol=COLOR_GREEN;
680 break;
681 case 43:
682 s->t_attrib.bgcol=COLOR_YELLOW;
683 break;
684 case 44:
685 s->t_attrib.bgcol=COLOR_BLUE;
686 break;
687 case 45:
688 s->t_attrib.bgcol=COLOR_MAGENTA;
689 break;
690 case 46:
691 s->t_attrib.bgcol=COLOR_CYAN;
692 break;
693 case 47:
694 s->t_attrib.bgcol=COLOR_WHITE;
695 break;
696 }
697 }
698 }
699
700 static void console_putchar(TextConsole *s, int ch)
701 {
702 TextCell *c;
703 int y1, i, x;
704
705 switch(s->state) {
706 case TTY_STATE_NORM:
707 switch(ch) {
708 case '\r': /* carriage return */
709 s->x = 0;
710 break;
711 case '\n': /* newline */
712 console_put_lf(s);
713 break;
714 case '\b': /* backspace */
715 if(s->x > 0) s->x--;
716 y1 = (s->y_base + s->y) % s->total_height;
717 c = &s->cells[y1 * s->width + s->x];
718 c->ch = ' ';
719 c->t_attrib = s->t_attrib;
720 update_xy(s, s->x, s->y);
721 break;
722 case '\t': /* tabspace */
723 if (s->x + (8 - (s->x % 8)) > s->width) {
724 console_put_lf(s);
725 } else {
726 s->x = s->x + (8 - (s->x % 8));
727 }
728 break;
729 case '\a': /* alert aka. bell */
730 /* TODO: has to be implemented */
731 break;
732 case 27: /* esc (introducing an escape sequence) */
733 s->state = TTY_STATE_ESC;
734 break;
735 default:
736 y1 = (s->y_base + s->y) % s->total_height;
737 c = &s->cells[y1 * s->width + s->x];
738 c->ch = ch;
739 c->t_attrib = s->t_attrib;
740 update_xy(s, s->x, s->y);
741 s->x++;
742 if (s->x >= s->width)
743 console_put_lf(s);
744 break;
745 }
746 break;
747 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
748 if (ch == '[') {
749 for(i=0;i<MAX_ESC_PARAMS;i++)
750 s->esc_params[i] = 0;
751 s->nb_esc_params = 0;
752 s->state = TTY_STATE_CSI;
753 } else {
754 s->state = TTY_STATE_NORM;
755 }
756 break;
757 case TTY_STATE_CSI: /* handle escape sequence parameters */
758 if (ch >= '0' && ch <= '9') {
759 if (s->nb_esc_params < MAX_ESC_PARAMS) {
760 s->esc_params[s->nb_esc_params] =
761 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
762 }
763 } else {
764 s->nb_esc_params++;
765 if (ch == ';')
766 break;
767 s->state = TTY_STATE_NORM;
768 switch(ch) {
769 case 'D':
770 if (s->x > 0)
771 s->x--;
772 break;
773 case 'C':
774 if (s->x < (s->width - 1))
775 s->x++;
776 break;
777 case 'K':
778 /* clear to eol */
779 y1 = (s->y_base + s->y) % s->total_height;
780 for(x = s->x; x < s->width; x++) {
781 c = &s->cells[y1 * s->width + x];
782 c->ch = ' ';
783 c->t_attrib = s->t_attrib_default;
784 c++;
785 update_xy(s, x, s->y);
786 }
787 break;
788 default:
789 break;
790 }
791 console_handle_escape(s);
792 break;
793 }
794 }
795 }
796
797 void console_select(unsigned int index)
798 {
799 TextConsole *s;
800
801 if (index >= MAX_CONSOLES)
802 return;
803 s = consoles[index];
804 if (s) {
805 active_console = s;
806 if (s->text_console) {
807 if (s->g_width != s->ds->width ||
808 s->g_height != s->ds->height) {
809 s->g_width = s->ds->width;
810 s->g_height = s->ds->height;
811 text_console_resize(s);
812 }
813 console_refresh(s);
814 } else {
815 vga_hw_invalidate();
816 }
817 }
818 }
819
820 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
821 {
822 TextConsole *s = chr->opaque;
823 int i;
824
825 console_show_cursor(s, 0);
826 for(i = 0; i < len; i++) {
827 console_putchar(s, buf[i]);
828 }
829 console_show_cursor(s, 1);
830 return len;
831 }
832
833 static void console_chr_add_read_handler(CharDriverState *chr,
834 IOCanRWHandler *fd_can_read,
835 IOReadHandler *fd_read, void *opaque)
836 {
837 TextConsole *s = chr->opaque;
838 s->fd_read = fd_read;
839 s->fd_opaque = opaque;
840 }
841
842 static void console_send_event(CharDriverState *chr, int event)
843 {
844 TextConsole *s = chr->opaque;
845 int i;
846
847 if (event == CHR_EVENT_FOCUS) {
848 for(i = 0; i < nb_consoles; i++) {
849 if (consoles[i] == s) {
850 console_select(i);
851 break;
852 }
853 }
854 }
855 }
856
857 /* called when an ascii key is pressed */
858 void kbd_put_keysym(int keysym)
859 {
860 TextConsole *s;
861 uint8_t buf[16], *q;
862 int c;
863
864 s = active_console;
865 if (!s || !s->text_console)
866 return;
867
868 switch(keysym) {
869 case QEMU_KEY_CTRL_UP:
870 console_scroll(-1);
871 break;
872 case QEMU_KEY_CTRL_DOWN:
873 console_scroll(1);
874 break;
875 case QEMU_KEY_CTRL_PAGEUP:
876 console_scroll(-10);
877 break;
878 case QEMU_KEY_CTRL_PAGEDOWN:
879 console_scroll(10);
880 break;
881 default:
882 if (s->fd_read) {
883 /* convert the QEMU keysym to VT100 key string */
884 q = buf;
885 if (keysym >= 0xe100 && keysym <= 0xe11f) {
886 *q++ = '\033';
887 *q++ = '[';
888 c = keysym - 0xe100;
889 if (c >= 10)
890 *q++ = '0' + (c / 10);
891 *q++ = '0' + (c % 10);
892 *q++ = '~';
893 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
894 *q++ = '\033';
895 *q++ = '[';
896 *q++ = keysym & 0xff;
897 } else {
898 *q++ = keysym;
899 }
900 s->fd_read(s->fd_opaque, buf, q - buf);
901 }
902 break;
903 }
904 }
905
906 static TextConsole *new_console(DisplayState *ds, int text)
907 {
908 TextConsole *s;
909 int i;
910
911 if (nb_consoles >= MAX_CONSOLES)
912 return NULL;
913 s = qemu_mallocz(sizeof(TextConsole));
914 if (!s) {
915 return NULL;
916 }
917 if (!active_console || (active_console->text_console && !text))
918 active_console = s;
919 s->ds = ds;
920 s->text_console = text;
921 if (text) {
922 consoles[nb_consoles++] = s;
923 } else {
924 /* HACK: Put graphical consoles before text consoles. */
925 for (i = nb_consoles; i > 0; i--) {
926 if (!consoles[i - 1]->text_console)
927 break;
928 consoles[i] = consoles[i - 1];
929 }
930 consoles[i] = s;
931 }
932 return s;
933 }
934
935 TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
936 vga_hw_invalidate_ptr invalidate,
937 vga_hw_screen_dump_ptr screen_dump,
938 void *opaque)
939 {
940 TextConsole *s;
941
942 s = new_console(ds, 0);
943 if (!s)
944 return NULL;
945 s->hw_update = update;
946 s->hw_invalidate = invalidate;
947 s->hw_screen_dump = screen_dump;
948 s->hw = opaque;
949 return s;
950 }
951
952 int is_graphic_console(void)
953 {
954 return !active_console->text_console;
955 }
956
957 CharDriverState *text_console_init(DisplayState *ds)
958 {
959 CharDriverState *chr;
960 TextConsole *s;
961 int i,j;
962 static int color_inited;
963
964 chr = qemu_mallocz(sizeof(CharDriverState));
965 if (!chr)
966 return NULL;
967 s = new_console(ds, 1);
968 if (!s) {
969 free(chr);
970 return NULL;
971 }
972 chr->opaque = s;
973 chr->chr_write = console_puts;
974 chr->chr_add_read_handler = console_chr_add_read_handler;
975 chr->chr_send_event = console_send_event;
976
977 if (!color_inited) {
978 color_inited = 1;
979 for(j = 0; j < 2; j++) {
980 for(i = 0; i < 8; i++) {
981 color_table[j][i] = col_expand(s->ds,
982 vga_get_color(s->ds, color_table_rgb[j][i]));
983 }
984 }
985 }
986 s->y_displayed = 0;
987 s->y_base = 0;
988 s->total_height = DEFAULT_BACKSCROLL;
989 s->x = 0;
990 s->y = 0;
991 s->g_width = s->ds->width;
992 s->g_height = s->ds->height;
993
994 /* Set text attribute defaults */
995 s->t_attrib_default.bold = 0;
996 s->t_attrib_default.uline = 0;
997 s->t_attrib_default.blink = 0;
998 s->t_attrib_default.invers = 0;
999 s->t_attrib_default.unvisible = 0;
1000 s->t_attrib_default.fgcol = COLOR_WHITE;
1001 s->t_attrib_default.bgcol = COLOR_BLACK;
1002
1003 /* set current text attributes to default */
1004 s->t_attrib = s->t_attrib_default;
1005 text_console_resize(s);
1006
1007 return chr;
1008 }