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