Statistics
| Branch: | Revision:

root / console.c @ a528b80c

History | View | Annotate | Download (34.2 kB)

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
typedef enum {
108
    GRAPHIC_CONSOLE,
109
    TEXT_CONSOLE,
110
    TEXT_CONSOLE_FIXED_SIZE
111
} console_type_t;
112

    
113
/* ??? This is mis-named.
114
   It is used for both text and graphical consoles.  */
115
struct TextConsole {
116
    console_type_t console_type;
117
    DisplayState *ds;
118
    /* Graphic console state.  */
119
    vga_hw_update_ptr hw_update;
120
    vga_hw_invalidate_ptr hw_invalidate;
121
    vga_hw_screen_dump_ptr hw_screen_dump;
122
    void *hw;
123

    
124
    int g_width, g_height;
125
    int width;
126
    int height;
127
    int total_height;
128
    int backscroll_height;
129
    int x, y;
130
    int x_saved, y_saved;
131
    int y_displayed;
132
    int y_base;
133
    TextAttributes t_attrib_default; /* default text attributes */
134
    TextAttributes t_attrib; /* currently active text attributes */
135
    TextCell *cells;
136

    
137
    enum TTYState state;
138
    int esc_params[MAX_ESC_PARAMS];
139
    int nb_esc_params;
140

    
141
    CharDriverState *chr;
142
    /* fifo for key pressed */
143
    QEMUFIFO out_fifo;
144
    uint8_t out_fifo_buf[16];
145
    QEMUTimer *kbd_timer;
146
};
147

    
148
static TextConsole *active_console;
149
static TextConsole *consoles[MAX_CONSOLES];
150
static int nb_consoles = 0;
151

    
152
void vga_hw_update(void)
153
{
154
    if (active_console && active_console->hw_update)
155
        active_console->hw_update(active_console->hw);
156
}
157

    
158
void vga_hw_invalidate(void)
159
{
160
    if (active_console->hw_invalidate)
161
        active_console->hw_invalidate(active_console->hw);
162
}
163

    
164
void vga_hw_screen_dump(const char *filename)
165
{
166
    /* There is currently no was of specifying which screen we want to dump,
167
       so always dump the dirst one.  */
168
    if (consoles[0]->hw_screen_dump)
169
        consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
170
}
171

    
172
/* convert a RGBA color to a color index usable in graphic primitives */
173
static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
174
{
175
    unsigned int r, g, b, color;
176

    
177
    switch(ds->depth) {
178
#if 0
179
    case 8:
180
        r = (rgba >> 16) & 0xff;
181
        g = (rgba >> 8) & 0xff;
182
        b = (rgba) & 0xff;
183
        color = (rgb_to_index[r] * 6 * 6) +
184
            (rgb_to_index[g] * 6) +
185
            (rgb_to_index[b]);
186
        break;
187
#endif
188
    case 15:
189
        r = (rgba >> 16) & 0xff;
190
        g = (rgba >> 8) & 0xff;
191
        b = (rgba) & 0xff;
192
        color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
193
        break;
194
    case 16:
195
        r = (rgba >> 16) & 0xff;
196
        g = (rgba >> 8) & 0xff;
197
        b = (rgba) & 0xff;
198
        color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
199
        break;
200
    case 32:
201
    default:
202
        color = rgba;
203
        break;
204
    }
205
    return color;
206
}
207

    
208
static void vga_fill_rect (DisplayState *ds,
209
                           int posx, int posy, int width, int height, uint32_t color)
210
{
211
    uint8_t *d, *d1;
212
    int x, y, bpp;
213

    
214
    bpp = (ds->depth + 7) >> 3;
215
    d1 = ds->data +
216
        ds->linesize * posy + bpp * posx;
217
    for (y = 0; y < height; y++) {
218
        d = d1;
219
        switch(bpp) {
220
        case 1:
221
            for (x = 0; x < width; x++) {
222
                *((uint8_t *)d) = color;
223
                d++;
224
            }
225
            break;
226
        case 2:
227
            for (x = 0; x < width; x++) {
228
                *((uint16_t *)d) = color;
229
                d += 2;
230
            }
231
            break;
232
        case 4:
233
            for (x = 0; x < width; x++) {
234
                *((uint32_t *)d) = color;
235
                d += 4;
236
            }
237
            break;
238
        }
239
        d1 += ds->linesize;
240
    }
241
}
242

    
243
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
244
static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
245
{
246
    const uint8_t *s;
247
    uint8_t *d;
248
    int wb, y, bpp;
249

    
250
    bpp = (ds->depth + 7) >> 3;
251
    wb = w * bpp;
252
    if (yd <= ys) {
253
        s = ds->data +
254
            ds->linesize * ys + bpp * xs;
255
        d = ds->data +
256
            ds->linesize * yd + bpp * xd;
257
        for (y = 0; y < h; y++) {
258
            memmove(d, s, wb);
259
            d += ds->linesize;
260
            s += ds->linesize;
261
        }
262
    } else {
263
        s = ds->data +
264
            ds->linesize * (ys + h - 1) + bpp * xs;
265
        d = ds->data +
266
            ds->linesize * (yd + h - 1) + bpp * xd;
267
       for (y = 0; y < h; y++) {
268
            memmove(d, s, wb);
269
            d -= ds->linesize;
270
            s -= ds->linesize;
271
        }
272
    }
273
}
274

    
275
/***********************************************************/
276
/* basic char display */
277

    
278
#define FONT_HEIGHT 16
279
#define FONT_WIDTH 8
280

    
281
#include "vgafont.h"
282

    
283
#define cbswap_32(__x) \
284
((uint32_t)( \
285
                (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
286
                (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
287
                (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
288
                (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
289

    
290
#ifdef WORDS_BIGENDIAN
291
#define PAT(x) x
292
#else
293
#define PAT(x) cbswap_32(x)
294
#endif
295

    
296
static const uint32_t dmask16[16] = {
297
    PAT(0x00000000),
298
    PAT(0x000000ff),
299
    PAT(0x0000ff00),
300
    PAT(0x0000ffff),
301
    PAT(0x00ff0000),
302
    PAT(0x00ff00ff),
303
    PAT(0x00ffff00),
304
    PAT(0x00ffffff),
305
    PAT(0xff000000),
306
    PAT(0xff0000ff),
307
    PAT(0xff00ff00),
308
    PAT(0xff00ffff),
309
    PAT(0xffff0000),
310
    PAT(0xffff00ff),
311
    PAT(0xffffff00),
312
    PAT(0xffffffff),
313
};
314

    
315
static const uint32_t dmask4[4] = {
316
    PAT(0x00000000),
317
    PAT(0x0000ffff),
318
    PAT(0xffff0000),
319
    PAT(0xffffffff),
320
};
321

    
322
static uint32_t color_table[2][8];
323

    
324
enum color_names {
325
    COLOR_BLACK   = 0,
326
    COLOR_RED     = 1,
327
    COLOR_GREEN   = 2,
328
    COLOR_YELLOW  = 3,
329
    COLOR_BLUE    = 4,
330
    COLOR_MAGENTA = 5,
331
    COLOR_CYAN    = 6,
332
    COLOR_WHITE   = 7
333
};
334

    
335
static const uint32_t color_table_rgb[2][8] = {
336
    {   /* dark */
337
        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
338
        QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
339
        QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
340
        QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
341
        QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
342
        QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
343
        QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
344
        QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
345
    },
346
    {   /* bright */
347
        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
348
        QEMU_RGB(0xff, 0x00, 0x00),  /* red */
349
        QEMU_RGB(0x00, 0xff, 0x00),  /* green */
350
        QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
351
        QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
352
        QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
353
        QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
354
        QEMU_RGB(0xff, 0xff, 0xff),  /* white */
355
    }
356
};
357

    
358
static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
359
{
360
    switch(ds->depth) {
361
    case 8:
362
        col |= col << 8;
363
        col |= col << 16;
364
        break;
365
    case 15:
366
    case 16:
367
        col |= col << 16;
368
        break;
369
    default:
370
        break;
371
    }
372

    
373
    return col;
374
}
375
#ifdef DEBUG_CONSOLE
376
static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
377
{
378
    if (t_attrib->bold) {
379
        printf("b");
380
    } else {
381
        printf(" ");
382
    }
383
    if (t_attrib->uline) {
384
        printf("u");
385
    } else {
386
        printf(" ");
387
    }
388
    if (t_attrib->blink) {
389
        printf("l");
390
    } else {
391
        printf(" ");
392
    }
393
    if (t_attrib->invers) {
394
        printf("i");
395
    } else {
396
        printf(" ");
397
    }
398
    if (t_attrib->unvisible) {
399
        printf("n");
400
    } else {
401
        printf(" ");
402
    }
403

    
404
    printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
405
}
406
#endif
407

    
408
static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
409
                          TextAttributes *t_attrib)
410
{
411
    uint8_t *d;
412
    const uint8_t *font_ptr;
413
    unsigned int font_data, linesize, xorcol, bpp;
414
    int i;
415
    unsigned int fgcol, bgcol;
416

    
417
#ifdef DEBUG_CONSOLE
418
    printf("x: %2i y: %2i", x, y);
419
    console_print_text_attributes(t_attrib, ch);
420
#endif
421

    
422
    if (t_attrib->invers) {
423
        bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
424
        fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
425
    } else {
426
        fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
427
        bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
428
    }
429

    
430
    bpp = (ds->depth + 7) >> 3;
431
    d = ds->data +
432
        ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
433
    linesize = ds->linesize;
434
    font_ptr = vgafont16 + FONT_HEIGHT * ch;
435
    xorcol = bgcol ^ fgcol;
436
    switch(ds->depth) {
437
    case 8:
438
        for(i = 0; i < FONT_HEIGHT; i++) {
439
            font_data = *font_ptr++;
440
            if (t_attrib->uline
441
                && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
442
                font_data = 0xFFFF;
443
            }
444
            ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
445
            ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
446
            d += linesize;
447
        }
448
        break;
449
    case 16:
450
    case 15:
451
        for(i = 0; i < FONT_HEIGHT; i++) {
452
            font_data = *font_ptr++;
453
            if (t_attrib->uline
454
                && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
455
                font_data = 0xFFFF;
456
            }
457
            ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
458
            ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
459
            ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
460
            ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
461
            d += linesize;
462
        }
463
        break;
464
    case 32:
465
        for(i = 0; i < FONT_HEIGHT; i++) {
466
            font_data = *font_ptr++;
467
            if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
468
                font_data = 0xFFFF;
469
            }
470
            ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
471
            ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
472
            ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
473
            ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
474
            ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
475
            ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
476
            ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
477
            ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
478
            d += linesize;
479
        }
480
        break;
481
    }
482
}
483

    
484
static void text_console_resize(TextConsole *s)
485
{
486
    TextCell *cells, *c, *c1;
487
    int w1, x, y, last_width;
488

    
489
    last_width = s->width;
490
    s->width = s->g_width / FONT_WIDTH;
491
    s->height = s->g_height / FONT_HEIGHT;
492

    
493
    w1 = last_width;
494
    if (s->width < w1)
495
        w1 = s->width;
496

    
497
    cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
498
    for(y = 0; y < s->total_height; y++) {
499
        c = &cells[y * s->width];
500
        if (w1 > 0) {
501
            c1 = &s->cells[y * last_width];
502
            for(x = 0; x < w1; x++) {
503
                *c++ = *c1++;
504
            }
505
        }
506
        for(x = w1; x < s->width; x++) {
507
            c->ch = ' ';
508
            c->t_attrib = s->t_attrib_default;
509
            c++;
510
        }
511
    }
512
    qemu_free(s->cells);
513
    s->cells = cells;
514
}
515

    
516
static void update_xy(TextConsole *s, int x, int y)
517
{
518
    TextCell *c;
519
    int y1, y2;
520

    
521
    if (s == active_console) {
522
        y1 = (s->y_base + y) % s->total_height;
523
        y2 = y1 - s->y_displayed;
524
        if (y2 < 0)
525
            y2 += s->total_height;
526
        if (y2 < s->height) {
527
            c = &s->cells[y1 * s->width + x];
528
            vga_putcharxy(s->ds, x, y2, c->ch,
529
                          &(c->t_attrib));
530
            dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
531
                       FONT_WIDTH, FONT_HEIGHT);
532
        }
533
    }
534
}
535

    
536
static void console_show_cursor(TextConsole *s, int show)
537
{
538
    TextCell *c;
539
    int y, y1;
540

    
541
    if (s == active_console) {
542
        int x = s->x;
543
        if (x >= s->width) {
544
            x = s->width - 1;
545
        }
546
        y1 = (s->y_base + s->y) % s->total_height;
547
        y = y1 - s->y_displayed;
548
        if (y < 0)
549
            y += s->total_height;
550
        if (y < s->height) {
551
            c = &s->cells[y1 * s->width + x];
552
            if (show) {
553
                TextAttributes t_attrib = s->t_attrib_default;
554
                t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
555
                vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
556
            } else {
557
                vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
558
            }
559
            dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
560
                       FONT_WIDTH, FONT_HEIGHT);
561
        }
562
    }
563
}
564

    
565
static void console_refresh(TextConsole *s)
566
{
567
    TextCell *c;
568
    int x, y, y1;
569

    
570
    if (s != active_console)
571
        return;
572

    
573
    vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
574
                  color_table[0][COLOR_BLACK]);
575
    y1 = s->y_displayed;
576
    for(y = 0; y < s->height; y++) {
577
        c = s->cells + y1 * s->width;
578
        for(x = 0; x < s->width; x++) {
579
            vga_putcharxy(s->ds, x, y, c->ch,
580
                          &(c->t_attrib));
581
            c++;
582
        }
583
        if (++y1 == s->total_height)
584
            y1 = 0;
585
    }
586
    dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
587
    console_show_cursor(s, 1);
588
}
589

    
590
static void console_scroll(int ydelta)
591
{
592
    TextConsole *s;
593
    int i, y1;
594

    
595
    s = active_console;
596
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
597
        return;
598

    
599
    if (ydelta > 0) {
600
        for(i = 0; i < ydelta; i++) {
601
            if (s->y_displayed == s->y_base)
602
                break;
603
            if (++s->y_displayed == s->total_height)
604
                s->y_displayed = 0;
605
        }
606
    } else {
607
        ydelta = -ydelta;
608
        i = s->backscroll_height;
609
        if (i > s->total_height - s->height)
610
            i = s->total_height - s->height;
611
        y1 = s->y_base - i;
612
        if (y1 < 0)
613
            y1 += s->total_height;
614
        for(i = 0; i < ydelta; i++) {
615
            if (s->y_displayed == y1)
616
                break;
617
            if (--s->y_displayed < 0)
618
                s->y_displayed = s->total_height - 1;
619
        }
620
    }
621
    console_refresh(s);
622
}
623

    
624
static void console_put_lf(TextConsole *s)
625
{
626
    TextCell *c;
627
    int x, y1;
628

    
629
    s->y++;
630
    if (s->y >= s->height) {
631
        s->y = s->height - 1;
632

    
633
        if (s->y_displayed == s->y_base) {
634
            if (++s->y_displayed == s->total_height)
635
                s->y_displayed = 0;
636
        }
637
        if (++s->y_base == s->total_height)
638
            s->y_base = 0;
639
        if (s->backscroll_height < s->total_height)
640
            s->backscroll_height++;
641
        y1 = (s->y_base + s->height - 1) % s->total_height;
642
        c = &s->cells[y1 * s->width];
643
        for(x = 0; x < s->width; x++) {
644
            c->ch = ' ';
645
            c->t_attrib = s->t_attrib_default;
646
            c++;
647
        }
648
        if (s == active_console && s->y_displayed == s->y_base) {
649
            vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
650
                       s->width * FONT_WIDTH,
651
                       (s->height - 1) * FONT_HEIGHT);
652
            vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
653
                          s->width * FONT_WIDTH, FONT_HEIGHT,
654
                          color_table[0][s->t_attrib_default.bgcol]);
655
            dpy_update(s->ds, 0, 0,
656
                       s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
657
        }
658
    }
659
}
660

    
661
/* Set console attributes depending on the current escape codes.
662
 * NOTE: I know this code is not very efficient (checking every color for it
663
 * self) but it is more readable and better maintainable.
664
 */
665
static void console_handle_escape(TextConsole *s)
666
{
667
    int i;
668

    
669
    for (i=0; i<s->nb_esc_params; i++) {
670
        switch (s->esc_params[i]) {
671
            case 0: /* reset all console attributes to default */
672
                s->t_attrib = s->t_attrib_default;
673
                break;
674
            case 1:
675
                s->t_attrib.bold = 1;
676
                break;
677
            case 4:
678
                s->t_attrib.uline = 1;
679
                break;
680
            case 5:
681
                s->t_attrib.blink = 1;
682
                break;
683
            case 7:
684
                s->t_attrib.invers = 1;
685
                break;
686
            case 8:
687
                s->t_attrib.unvisible = 1;
688
                break;
689
            case 22:
690
                s->t_attrib.bold = 0;
691
                break;
692
            case 24:
693
                s->t_attrib.uline = 0;
694
                break;
695
            case 25:
696
                s->t_attrib.blink = 0;
697
                break;
698
            case 27:
699
                s->t_attrib.invers = 0;
700
                break;
701
            case 28:
702
                s->t_attrib.unvisible = 0;
703
                break;
704
            /* set foreground color */
705
            case 30:
706
                s->t_attrib.fgcol=COLOR_BLACK;
707
                break;
708
            case 31:
709
                s->t_attrib.fgcol=COLOR_RED;
710
                break;
711
            case 32:
712
                s->t_attrib.fgcol=COLOR_GREEN;
713
                break;
714
            case 33:
715
                s->t_attrib.fgcol=COLOR_YELLOW;
716
                break;
717
            case 34:
718
                s->t_attrib.fgcol=COLOR_BLUE;
719
                break;
720
            case 35:
721
                s->t_attrib.fgcol=COLOR_MAGENTA;
722
                break;
723
            case 36:
724
                s->t_attrib.fgcol=COLOR_CYAN;
725
                break;
726
            case 37:
727
                s->t_attrib.fgcol=COLOR_WHITE;
728
                break;
729
            /* set background color */
730
            case 40:
731
                s->t_attrib.bgcol=COLOR_BLACK;
732
                break;
733
            case 41:
734
                s->t_attrib.bgcol=COLOR_RED;
735
                break;
736
            case 42:
737
                s->t_attrib.bgcol=COLOR_GREEN;
738
                break;
739
            case 43:
740
                s->t_attrib.bgcol=COLOR_YELLOW;
741
                break;
742
            case 44:
743
                s->t_attrib.bgcol=COLOR_BLUE;
744
                break;
745
            case 45:
746
                s->t_attrib.bgcol=COLOR_MAGENTA;
747
                break;
748
            case 46:
749
                s->t_attrib.bgcol=COLOR_CYAN;
750
                break;
751
            case 47:
752
                s->t_attrib.bgcol=COLOR_WHITE;
753
                break;
754
        }
755
    }
756
}
757

    
758
static void console_clear_xy(TextConsole *s, int x, int y)
759
{
760
    int y1 = (s->y_base + y) % s->total_height;
761
    TextCell *c = &s->cells[y1 * s->width + x];
762
    c->ch = ' ';
763
    c->t_attrib = s->t_attrib_default;
764
    c++;
765
    update_xy(s, x, y);
766
}
767

    
768
static void console_putchar(TextConsole *s, int ch)
769
{
770
    TextCell *c;
771
    int y1, i;
772
    int x, y;
773

    
774
    switch(s->state) {
775
    case TTY_STATE_NORM:
776
        switch(ch) {
777
        case '\r':  /* carriage return */
778
            s->x = 0;
779
            break;
780
        case '\n':  /* newline */
781
            console_put_lf(s);
782
            break;
783
        case '\b':  /* backspace */
784
            if (s->x > 0)
785
                s->x--;
786
            break;
787
        case '\t':  /* tabspace */
788
            if (s->x + (8 - (s->x % 8)) > s->width) {
789
                s->x = 0;
790
                console_put_lf(s);
791
            } else {
792
                s->x = s->x + (8 - (s->x % 8));
793
            }
794
            break;
795
        case '\a':  /* alert aka. bell */
796
            /* TODO: has to be implemented */
797
            break;
798
        case 14:
799
            /* SI (shift in), character set 0 (ignored) */
800
            break;
801
        case 15:
802
            /* SO (shift out), character set 1 (ignored) */
803
            break;
804
        case 27:    /* esc (introducing an escape sequence) */
805
            s->state = TTY_STATE_ESC;
806
            break;
807
        default:
808
            if (s->x >= s->width) {
809
                /* line wrap */
810
                s->x = 0;
811
                console_put_lf(s);
812
            }
813
            y1 = (s->y_base + s->y) % s->total_height;
814
            c = &s->cells[y1 * s->width + s->x];
815
            c->ch = ch;
816
            c->t_attrib = s->t_attrib;
817
            update_xy(s, s->x, s->y);
818
            s->x++;
819
            break;
820
        }
821
        break;
822
    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
823
        if (ch == '[') {
824
            for(i=0;i<MAX_ESC_PARAMS;i++)
825
                s->esc_params[i] = 0;
826
            s->nb_esc_params = 0;
827
            s->state = TTY_STATE_CSI;
828
        } else {
829
            s->state = TTY_STATE_NORM;
830
        }
831
        break;
832
    case TTY_STATE_CSI: /* handle escape sequence parameters */
833
        if (ch >= '0' && ch <= '9') {
834
            if (s->nb_esc_params < MAX_ESC_PARAMS) {
835
                s->esc_params[s->nb_esc_params] =
836
                    s->esc_params[s->nb_esc_params] * 10 + ch - '0';
837
            }
838
        } else {
839
            s->nb_esc_params++;
840
            if (ch == ';')
841
                break;
842
#ifdef DEBUG_CONSOLE
843
            fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
844
                    s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
845
#endif
846
            s->state = TTY_STATE_NORM;
847
            switch(ch) {
848
            case 'A':
849
                /* move cursor up */
850
                if (s->esc_params[0] == 0) {
851
                    s->esc_params[0] = 1;
852
                }
853
                s->y -= s->esc_params[0];
854
                if (s->y < 0) {
855
                    s->y = 0;
856
                }
857
                break;
858
            case 'B':
859
                /* move cursor down */
860
                if (s->esc_params[0] == 0) {
861
                    s->esc_params[0] = 1;
862
                }
863
                s->y += s->esc_params[0];
864
                if (s->y >= s->height) {
865
                    s->y = s->height - 1;
866
                }
867
                break;
868
            case 'C':
869
                /* move cursor right */
870
                if (s->esc_params[0] == 0) {
871
                    s->esc_params[0] = 1;
872
                }
873
                s->x += s->esc_params[0];
874
                if (s->x >= s->width) {
875
                    s->x = s->width - 1;
876
                }
877
                break;
878
            case 'D':
879
                /* move cursor left */
880
                if (s->esc_params[0] == 0) {
881
                    s->esc_params[0] = 1;
882
                }
883
                s->x -= s->esc_params[0];
884
                if (s->x < 0) {
885
                    s->x = 0;
886
                }
887
                break;
888
            case 'G':
889
                /* move cursor to column */
890
                s->x = s->esc_params[0] - 1;
891
                if (s->x < 0) {
892
                    s->x = 0;
893
                }
894
                break;
895
            case 'f':
896
            case 'H':
897
                /* move cursor to row, column */
898
                s->x = s->esc_params[1] - 1;
899
                if (s->x < 0) {
900
                    s->x = 0;
901
                }
902
                s->y = s->esc_params[0] - 1;
903
                if (s->y < 0) {
904
                    s->y = 0;
905
                }
906
                break;
907
            case 'J':
908
                switch (s->esc_params[0]) {
909
                case 0:
910
                    /* clear to end of screen */
911
                    for (y = s->y; y < s->height; y++) {
912
                        for (x = 0; x < s->width; x++) {
913
                            if (y == s->y && x < s->x) {
914
                                continue;
915
                            }
916
                            console_clear_xy(s, x, y);
917
                        }
918
                    }
919
                    break;
920
                case 1:
921
                    /* clear from beginning of screen */
922
                    for (y = 0; y <= s->y; y++) {
923
                        for (x = 0; x < s->width; x++) {
924
                            if (y == s->y && x > s->x) {
925
                                break;
926
                            }
927
                            console_clear_xy(s, x, y);
928
                        }
929
                    }
930
                    break;
931
                case 2:
932
                    /* clear entire screen */
933
                    for (y = 0; y <= s->height; y++) {
934
                        for (x = 0; x < s->width; x++) {
935
                            console_clear_xy(s, x, y);
936
                        }
937
                    }
938
                break;
939
                }
940
            case 'K':
941
                switch (s->esc_params[0]) {
942
                case 0:
943
                /* clear to eol */
944
                for(x = s->x; x < s->width; x++) {
945
                        console_clear_xy(s, x, s->y);
946
                }
947
                break;
948
                case 1:
949
                    /* clear from beginning of line */
950
                    for (x = 0; x <= s->x; x++) {
951
                        console_clear_xy(s, x, s->y);
952
                    }
953
                    break;
954
                case 2:
955
                    /* clear entire line */
956
                    for(x = 0; x < s->width; x++) {
957
                        console_clear_xy(s, x, s->y);
958
                    }
959
                break;
960
            }
961
                break;
962
            case 'm':
963
            console_handle_escape(s);
964
            break;
965
            case 'n':
966
                /* report cursor position */
967
                /* TODO: send ESC[row;colR */
968
                break;
969
            case 's':
970
                /* save cursor position */
971
                s->x_saved = s->x;
972
                s->y_saved = s->y;
973
                break;
974
            case 'u':
975
                /* restore cursor position */
976
                s->x = s->x_saved;
977
                s->y = s->y_saved;
978
                break;
979
            default:
980
#ifdef DEBUG_CONSOLE
981
                fprintf(stderr, "unhandled escape character '%c'\n", ch);
982
#endif
983
                break;
984
            }
985
            break;
986
        }
987
    }
988
}
989

    
990
void console_select(unsigned int index)
991
{
992
    TextConsole *s;
993

    
994
    if (index >= MAX_CONSOLES)
995
        return;
996
    s = consoles[index];
997
    if (s) {
998
        active_console = s;
999
        if (s->console_type != GRAPHIC_CONSOLE) {
1000
            if (s->g_width != s->ds->width ||
1001
                s->g_height != s->ds->height) {
1002
                if (s->console_type == TEXT_CONSOLE_FIXED_SIZE) {
1003
                    dpy_resize(s->ds, s->g_width, s->g_height);
1004
                } else {
1005
                s->g_width = s->ds->width;
1006
                s->g_height = s->ds->height;
1007
                text_console_resize(s);
1008
            }
1009
            }
1010
            console_refresh(s);
1011
        } else {
1012
            vga_hw_invalidate();
1013
        }
1014
    }
1015
}
1016

    
1017
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1018
{
1019
    TextConsole *s = chr->opaque;
1020
    int i;
1021

    
1022
    console_show_cursor(s, 0);
1023
    for(i = 0; i < len; i++) {
1024
        console_putchar(s, buf[i]);
1025
    }
1026
    console_show_cursor(s, 1);
1027
    return len;
1028
}
1029

    
1030
static void console_send_event(CharDriverState *chr, int event)
1031
{
1032
    TextConsole *s = chr->opaque;
1033
    int i;
1034

    
1035
    if (event == CHR_EVENT_FOCUS) {
1036
        for(i = 0; i < nb_consoles; i++) {
1037
            if (consoles[i] == s) {
1038
                console_select(i);
1039
                break;
1040
            }
1041
        }
1042
    }
1043
}
1044

    
1045
static void kbd_send_chars(void *opaque)
1046
{
1047
    TextConsole *s = opaque;
1048
    int len;
1049
    uint8_t buf[16];
1050

    
1051
    len = qemu_chr_can_read(s->chr);
1052
    if (len > s->out_fifo.count)
1053
        len = s->out_fifo.count;
1054
    if (len > 0) {
1055
        if (len > sizeof(buf))
1056
            len = sizeof(buf);
1057
        qemu_fifo_read(&s->out_fifo, buf, len);
1058
        qemu_chr_read(s->chr, buf, len);
1059
    }
1060
    /* characters are pending: we send them a bit later (XXX:
1061
       horrible, should change char device API) */
1062
    if (s->out_fifo.count > 0) {
1063
        qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1064
    }
1065
}
1066

    
1067
/* called when an ascii key is pressed */
1068
void kbd_put_keysym(int keysym)
1069
{
1070
    TextConsole *s;
1071
    uint8_t buf[16], *q;
1072
    int c;
1073

    
1074
    s = active_console;
1075
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
1076
        return;
1077

    
1078
    switch(keysym) {
1079
    case QEMU_KEY_CTRL_UP:
1080
        console_scroll(-1);
1081
        break;
1082
    case QEMU_KEY_CTRL_DOWN:
1083
        console_scroll(1);
1084
        break;
1085
    case QEMU_KEY_CTRL_PAGEUP:
1086
        console_scroll(-10);
1087
        break;
1088
    case QEMU_KEY_CTRL_PAGEDOWN:
1089
        console_scroll(10);
1090
        break;
1091
    default:
1092
        /* convert the QEMU keysym to VT100 key string */
1093
        q = buf;
1094
        if (keysym >= 0xe100 && keysym <= 0xe11f) {
1095
            *q++ = '\033';
1096
            *q++ = '[';
1097
            c = keysym - 0xe100;
1098
            if (c >= 10)
1099
                *q++ = '0' + (c / 10);
1100
            *q++ = '0' + (c % 10);
1101
            *q++ = '~';
1102
        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1103
            *q++ = '\033';
1104
            *q++ = '[';
1105
            *q++ = keysym & 0xff;
1106
        } else {
1107
                *q++ = keysym;
1108
        }
1109
        if (s->chr->chr_read) {
1110
            qemu_fifo_write(&s->out_fifo, buf, q - buf);
1111
            kbd_send_chars(s);
1112
        }
1113
        break;
1114
    }
1115
}
1116

    
1117
static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1118
{
1119
    TextConsole *s;
1120
    int i;
1121

    
1122
    if (nb_consoles >= MAX_CONSOLES)
1123
        return NULL;
1124
    s = qemu_mallocz(sizeof(TextConsole));
1125
    if (!s) {
1126
        return NULL;
1127
    }
1128
    if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1129
        (console_type == GRAPHIC_CONSOLE))) {
1130
        active_console = s;
1131
    }
1132
    s->ds = ds;
1133
    s->console_type = console_type;
1134
    if (console_type != GRAPHIC_CONSOLE) {
1135
        consoles[nb_consoles++] = s;
1136
    } else {
1137
        /* HACK: Put graphical consoles before text consoles.  */
1138
        for (i = nb_consoles; i > 0; i--) {
1139
            if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1140
                break;
1141
            consoles[i] = consoles[i - 1];
1142
        }
1143
        consoles[i] = s;
1144
    }
1145
    return s;
1146
}
1147

    
1148
TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1149
                                  vga_hw_invalidate_ptr invalidate,
1150
                                  vga_hw_screen_dump_ptr screen_dump,
1151
                                  void *opaque)
1152
{
1153
    TextConsole *s;
1154

    
1155
    s = new_console(ds, GRAPHIC_CONSOLE);
1156
    if (!s)
1157
      return NULL;
1158
    s->hw_update = update;
1159
    s->hw_invalidate = invalidate;
1160
    s->hw_screen_dump = screen_dump;
1161
    s->hw = opaque;
1162
    return s;
1163
}
1164

    
1165
int is_graphic_console(void)
1166
{
1167
    return active_console->console_type == GRAPHIC_CONSOLE;
1168
}
1169

    
1170
void console_color_init(DisplayState *ds)
1171
{
1172
    int i, j;
1173
    for (j = 0; j < 2; j++) {
1174
        for (i = 0; i < 8; i++) {
1175
            color_table[j][i] = col_expand(ds, 
1176
                   vga_get_color(ds, color_table_rgb[j][i]));
1177
        }
1178
    }
1179
}
1180

    
1181
CharDriverState *text_console_init(DisplayState *ds, const char *p)
1182
{
1183
    CharDriverState *chr;
1184
    TextConsole *s;
1185
    unsigned width;
1186
    unsigned height;
1187
    static int color_inited;
1188

    
1189
    chr = qemu_mallocz(sizeof(CharDriverState));
1190
    if (!chr)
1191
        return NULL;
1192
    s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
1193
    if (!s) {
1194
        free(chr);
1195
        return NULL;
1196
    }
1197
    chr->opaque = s;
1198
    chr->chr_write = console_puts;
1199
    chr->chr_send_event = console_send_event;
1200

    
1201
    s->chr = chr;
1202
    s->out_fifo.buf = s->out_fifo_buf;
1203
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1204
    s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1205

    
1206
    if (!color_inited) {
1207
        color_inited = 1;
1208
        console_color_init(s->ds);
1209
    }
1210
    s->y_displayed = 0;
1211
    s->y_base = 0;
1212
    s->total_height = DEFAULT_BACKSCROLL;
1213
    s->x = 0;
1214
    s->y = 0;
1215
    width = s->ds->width;
1216
    height = s->ds->height;
1217
    if (p != 0) {
1218
        width = strtoul(p, (char **)&p, 10);
1219
        if (*p == 'C') {
1220
            p++;
1221
            width *= FONT_WIDTH;
1222
        }
1223
        if (*p == 'x') {
1224
            p++;
1225
            height = strtoul(p, (char **)&p, 10);
1226
            if (*p == 'C') {
1227
                p++;
1228
                height *= FONT_HEIGHT;
1229
            }
1230
        }
1231
    }
1232
    s->g_width = width;
1233
    s->g_height = height;
1234

    
1235
    /* Set text attribute defaults */
1236
    s->t_attrib_default.bold = 0;
1237
    s->t_attrib_default.uline = 0;
1238
    s->t_attrib_default.blink = 0;
1239
    s->t_attrib_default.invers = 0;
1240
    s->t_attrib_default.unvisible = 0;
1241
    s->t_attrib_default.fgcol = COLOR_WHITE;
1242
    s->t_attrib_default.bgcol = COLOR_BLACK;
1243

    
1244
    /* set current text attributes to default */
1245
    s->t_attrib = s->t_attrib_default;
1246
    text_console_resize(s);
1247

    
1248
    qemu_chr_reset(chr);
1249

    
1250
    return chr;
1251
}