Statistics
| Branch: | Revision:

root / console.c @ 12a4b2aa

History | View | Annotate | Download (33.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
/* ??? This is mis-named.
108
   It is used for both text and graphical consoles.  */
109
struct TextConsole {
110
    int text_console; /* true if text console */
111
    DisplayState *ds;
112
    /* Graphic console state.  */
113
    vga_hw_update_ptr hw_update;
114
    vga_hw_invalidate_ptr hw_invalidate;
115
    vga_hw_screen_dump_ptr hw_screen_dump;
116
    void *hw;
117

    
118
    int g_width, g_height;
119
    int width;
120
    int height;
121
    int total_height;
122
    int backscroll_height;
123
    int x, y;
124
    int x_saved, y_saved;
125
    int y_displayed;
126
    int y_base;
127
    TextAttributes t_attrib_default; /* default text attributes */
128
    TextAttributes t_attrib; /* currently active text attributes */
129
    TextCell *cells;
130

    
131
    enum TTYState state;
132
    int esc_params[MAX_ESC_PARAMS];
133
    int nb_esc_params;
134

    
135
    CharDriverState *chr;
136
    /* fifo for key pressed */
137
    QEMUFIFO out_fifo;
138
    uint8_t out_fifo_buf[16];
139
    QEMUTimer *kbd_timer;
140
};
141

    
142
static TextConsole *active_console;
143
static TextConsole *consoles[MAX_CONSOLES];
144
static int nb_consoles = 0;
145

    
146
void vga_hw_update(void)
147
{
148
    if (active_console && active_console->hw_update)
149
        active_console->hw_update(active_console->hw);
150
}
151

    
152
void vga_hw_invalidate(void)
153
{
154
    if (active_console->hw_invalidate)
155
        active_console->hw_invalidate(active_console->hw);
156
}
157

    
158
void vga_hw_screen_dump(const char *filename)
159
{
160
    /* There is currently no was of specifying which screen we want to dump,
161
       so always dump the dirst one.  */
162
    if (consoles[0]->hw_screen_dump)
163
        consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
164
}
165

    
166
/* convert a RGBA color to a color index usable in graphic primitives */
167
static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
168
{
169
    unsigned int r, g, b, color;
170

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

    
202
static void vga_fill_rect (DisplayState *ds, 
203
                           int posx, int posy, int width, int height, uint32_t color)
204
{
205
    uint8_t *d, *d1;
206
    int x, y, bpp;
207
    
208
    bpp = (ds->depth + 7) >> 3;
209
    d1 = ds->data + 
210
        ds->linesize * posy + bpp * posx;
211
    for (y = 0; y < height; y++) {
212
        d = d1;
213
        switch(bpp) {
214
        case 1:
215
            for (x = 0; x < width; x++) {
216
                *((uint8_t *)d) = color;
217
                d++;
218
            }
219
            break;
220
        case 2:
221
            for (x = 0; x < width; x++) {
222
                *((uint16_t *)d) = color;
223
                d += 2;
224
            }
225
            break;
226
        case 4:
227
            for (x = 0; x < width; x++) {
228
                *((uint32_t *)d) = color;
229
                d += 4;
230
            }
231
            break;
232
        }
233
        d1 += ds->linesize;
234
    }
235
}
236

    
237
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
238
static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
239
{
240
    const uint8_t *s;
241
    uint8_t *d;
242
    int wb, y, bpp;
243

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

    
269
/***********************************************************/
270
/* basic char display */
271

    
272
#define FONT_HEIGHT 16
273
#define FONT_WIDTH 8
274

    
275
#include "vgafont.h"
276

    
277
#define cbswap_32(__x) \
278
((uint32_t)( \
279
                (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
280
                (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
281
                (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
282
                (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
283

    
284
#ifdef WORDS_BIGENDIAN
285
#define PAT(x) x
286
#else
287
#define PAT(x) cbswap_32(x)
288
#endif
289

    
290
static const uint32_t dmask16[16] = {
291
    PAT(0x00000000),
292
    PAT(0x000000ff),
293
    PAT(0x0000ff00),
294
    PAT(0x0000ffff),
295
    PAT(0x00ff0000),
296
    PAT(0x00ff00ff),
297
    PAT(0x00ffff00),
298
    PAT(0x00ffffff),
299
    PAT(0xff000000),
300
    PAT(0xff0000ff),
301
    PAT(0xff00ff00),
302
    PAT(0xff00ffff),
303
    PAT(0xffff0000),
304
    PAT(0xffff00ff),
305
    PAT(0xffffff00),
306
    PAT(0xffffffff),
307
};
308

    
309
static const uint32_t dmask4[4] = {
310
    PAT(0x00000000),
311
    PAT(0x0000ffff),
312
    PAT(0xffff0000),
313
    PAT(0xffffffff),
314
};
315

    
316
static uint32_t color_table[2][8];
317

    
318
enum color_names {
319
    COLOR_BLACK   = 0,
320
    COLOR_RED     = 1,
321
    COLOR_GREEN   = 2,
322
    COLOR_YELLOW  = 3,
323
    COLOR_BLUE    = 4,
324
    COLOR_MAGENTA = 5,
325
    COLOR_CYAN    = 6,
326
    COLOR_WHITE   = 7
327
};
328

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

    
352
static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
353
{
354
    switch(ds->depth) {
355
    case 8:
356
        col |= col << 8;
357
        col |= col << 16;
358
        break;
359
    case 15:
360
    case 16:
361
        col |= col << 16;
362
        break;
363
    default:
364
        break;
365
    }
366

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

    
398
    printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
399
}
400
#endif
401

    
402
static void vga_putcharxy(DisplayState *ds, int x, int y, int ch, 
403
                          TextAttributes *t_attrib)
404
{
405
    uint8_t *d;
406
    const uint8_t *font_ptr;
407
    unsigned int font_data, linesize, xorcol, bpp;
408
    int i;
409
    unsigned int fgcol, bgcol;
410

    
411
#ifdef DEBUG_CONSOLE
412
    printf("x: %2i y: %2i", x, y);
413
    console_print_text_attributes(t_attrib, ch);
414
#endif
415

    
416
    if (t_attrib->invers) {
417
        bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
418
        fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
419
    } else {
420
        fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
421
        bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
422
    }
423

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

    
478
static void text_console_resize(TextConsole *s)
479
{
480
    TextCell *cells, *c, *c1;
481
    int w1, x, y, last_width;
482

    
483
    last_width = s->width;
484
    s->width = s->g_width / FONT_WIDTH;
485
    s->height = s->g_height / FONT_HEIGHT;
486

    
487
    w1 = last_width;
488
    if (s->width < w1)
489
        w1 = s->width;
490

    
491
    cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
492
    for(y = 0; y < s->total_height; y++) {
493
        c = &cells[y * s->width];
494
        if (w1 > 0) {
495
            c1 = &s->cells[y * last_width];
496
            for(x = 0; x < w1; x++) {
497
                *c++ = *c1++;
498
            }
499
        }
500
        for(x = w1; x < s->width; x++) {
501
            c->ch = ' ';
502
            c->t_attrib = s->t_attrib_default;
503
            c++;
504
        }
505
    }
506
    free(s->cells);
507
    s->cells = cells;
508
}
509

    
510
static void update_xy(TextConsole *s, int x, int y)
511
{
512
    TextCell *c;
513
    int y1, y2;
514

    
515
    if (s == active_console) {
516
        y1 = (s->y_base + y) % s->total_height;
517
        y2 = y1 - s->y_displayed;
518
        if (y2 < 0)
519
            y2 += s->total_height;
520
        if (y2 < s->height) {
521
            c = &s->cells[y1 * s->width + x];
522
            vga_putcharxy(s->ds, x, y2, c->ch, 
523
                          &(c->t_attrib));
524
            dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT, 
525
                       FONT_WIDTH, FONT_HEIGHT);
526
        }
527
    }
528
}
529

    
530
static void console_show_cursor(TextConsole *s, int show)
531
{
532
    TextCell *c;
533
    int y, y1;
534

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

    
559
static void console_refresh(TextConsole *s)
560
{
561
    TextCell *c;
562
    int x, y, y1;
563

    
564
    if (s != active_console) 
565
        return;
566

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

    
584
static void console_scroll(int ydelta)
585
{
586
    TextConsole *s;
587
    int i, y1;
588
    
589
    s = active_console;
590
    if (!s || !s->text_console)
591
        return;
592

    
593
    if (ydelta > 0) {
594
        for(i = 0; i < ydelta; i++) {
595
            if (s->y_displayed == s->y_base)
596
                break;
597
            if (++s->y_displayed == s->total_height)
598
                s->y_displayed = 0;
599
        }
600
    } else {
601
        ydelta = -ydelta;
602
        i = s->backscroll_height;
603
        if (i > s->total_height - s->height)
604
            i = s->total_height - s->height;
605
        y1 = s->y_base - i;
606
        if (y1 < 0)
607
            y1 += s->total_height;
608
        for(i = 0; i < ydelta; i++) {
609
            if (s->y_displayed == y1)
610
                break;
611
            if (--s->y_displayed < 0)
612
                s->y_displayed = s->total_height - 1;
613
        }
614
    }
615
    console_refresh(s);
616
}
617

    
618
static void console_put_lf(TextConsole *s)
619
{
620
    TextCell *c;
621
    int x, y1;
622

    
623
    s->y++;
624
    if (s->y >= s->height) {
625
        s->y = s->height - 1;
626

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

    
655
/* Set console attributes depending on the current escape codes.
656
 * NOTE: I know this code is not very efficient (checking every color for it
657
 * self) but it is more readable and better maintainable.
658
 */
659
static void console_handle_escape(TextConsole *s)
660
{
661
    int i;
662

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

    
752
static void console_clear_xy(TextConsole *s, int x, int y)
753
{
754
    int y1 = (s->y_base + y) % s->total_height;
755
    TextCell *c = &s->cells[y1 * s->width + x];
756
    c->ch = ' ';
757
    c->t_attrib = s->t_attrib_default;
758
    c++;
759
    update_xy(s, x, y);
760
}
761

    
762
static void console_putchar(TextConsole *s, int ch)
763
{
764
    TextCell *c;
765
    int y1, i;
766
    int x, y;
767

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

    
984
void console_select(unsigned int index)
985
{
986
    TextConsole *s;
987

    
988
    if (index >= MAX_CONSOLES)
989
        return;
990
    s = consoles[index];
991
    if (s) {
992
        active_console = s;
993
        if (s->text_console) {
994
            if (s->g_width != s->ds->width ||
995
                s->g_height != s->ds->height) {
996
                s->g_width = s->ds->width;
997
                s->g_height = s->ds->height;
998
                text_console_resize(s);
999
            }
1000
            console_refresh(s);
1001
        } else {
1002
            vga_hw_invalidate();
1003
        }
1004
    }
1005
}
1006

    
1007
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1008
{
1009
    TextConsole *s = chr->opaque;
1010
    int i;
1011

    
1012
    console_show_cursor(s, 0);
1013
    for(i = 0; i < len; i++) {
1014
        console_putchar(s, buf[i]);
1015
    }
1016
    console_show_cursor(s, 1);
1017
    return len;
1018
}
1019

    
1020
static void console_send_event(CharDriverState *chr, int event)
1021
{
1022
    TextConsole *s = chr->opaque;
1023
    int i;
1024

    
1025
    if (event == CHR_EVENT_FOCUS) {
1026
        for(i = 0; i < nb_consoles; i++) {
1027
            if (consoles[i] == s) {
1028
                console_select(i);
1029
                break;
1030
            }
1031
        }
1032
    }
1033
}
1034

    
1035
static void kbd_send_chars(void *opaque)
1036
{
1037
    TextConsole *s = opaque;
1038
    int len;
1039
    uint8_t buf[16];
1040
    
1041
    len = qemu_chr_can_read(s->chr);
1042
    if (len > s->out_fifo.count)
1043
        len = s->out_fifo.count;
1044
    if (len > 0) {
1045
        if (len > sizeof(buf))
1046
            len = sizeof(buf);
1047
        qemu_fifo_read(&s->out_fifo, buf, len);
1048
        qemu_chr_read(s->chr, buf, len);
1049
    }
1050
    /* characters are pending: we send them a bit later (XXX:
1051
       horrible, should change char device API) */
1052
    if (s->out_fifo.count > 0) {
1053
        qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1054
    }
1055
}
1056

    
1057
/* called when an ascii key is pressed */
1058
void kbd_put_keysym(int keysym)
1059
{
1060
    TextConsole *s;
1061
    uint8_t buf[16], *q;
1062
    int c;
1063

    
1064
    s = active_console;
1065
    if (!s || !s->text_console)
1066
        return;
1067

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

    
1107
static TextConsole *new_console(DisplayState *ds, int text)
1108
{
1109
    TextConsole *s;
1110
    int i;
1111

    
1112
    if (nb_consoles >= MAX_CONSOLES)
1113
        return NULL;
1114
    s = qemu_mallocz(sizeof(TextConsole));
1115
    if (!s) {
1116
        return NULL;
1117
    }
1118
    if (!active_console || (active_console->text_console && !text))
1119
        active_console = s;
1120
    s->ds = ds;
1121
    s->text_console = text;
1122
    if (text) {
1123
        consoles[nb_consoles++] = s;
1124
    } else {
1125
        /* HACK: Put graphical consoles before text consoles.  */
1126
        for (i = nb_consoles; i > 0; i--) {
1127
            if (!consoles[i - 1]->text_console)
1128
                break;
1129
            consoles[i] = consoles[i - 1];
1130
        }
1131
        consoles[i] = s;
1132
    }
1133
    return s;
1134
}
1135

    
1136
TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1137
                                  vga_hw_invalidate_ptr invalidate,
1138
                                  vga_hw_screen_dump_ptr screen_dump,
1139
                                  void *opaque)
1140
{
1141
    TextConsole *s;
1142

    
1143
    s = new_console(ds, 0);
1144
    if (!s)
1145
      return NULL;
1146
    s->hw_update = update;
1147
    s->hw_invalidate = invalidate;
1148
    s->hw_screen_dump = screen_dump;
1149
    s->hw = opaque;
1150
    return s;
1151
}
1152

    
1153
int is_graphic_console(void)
1154
{
1155
    return !active_console->text_console;
1156
}
1157

    
1158
CharDriverState *text_console_init(DisplayState *ds)
1159
{
1160
    CharDriverState *chr;
1161
    TextConsole *s;
1162
    int i,j;
1163
    static int color_inited;
1164

    
1165
    chr = qemu_mallocz(sizeof(CharDriverState));
1166
    if (!chr)
1167
        return NULL;
1168
    s = new_console(ds, 1);
1169
    if (!s) {
1170
        free(chr);
1171
        return NULL;
1172
    }
1173
    chr->opaque = s;
1174
    chr->chr_write = console_puts;
1175
    chr->chr_send_event = console_send_event;
1176

    
1177
    s->chr = chr;
1178
    s->out_fifo.buf = s->out_fifo_buf;
1179
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1180
    s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1181
    
1182
    if (!color_inited) {
1183
        color_inited = 1;
1184
        for(j = 0; j < 2; j++) {
1185
            for(i = 0; i < 8; i++) {
1186
                color_table[j][i] = col_expand(s->ds, 
1187
                        vga_get_color(s->ds, color_table_rgb[j][i]));
1188
            }
1189
        }
1190
    }
1191
    s->y_displayed = 0;
1192
    s->y_base = 0;
1193
    s->total_height = DEFAULT_BACKSCROLL;
1194
    s->x = 0;
1195
    s->y = 0;
1196
    s->g_width = s->ds->width;
1197
    s->g_height = s->ds->height;
1198

    
1199
    /* Set text attribute defaults */
1200
    s->t_attrib_default.bold = 0;
1201
    s->t_attrib_default.uline = 0;
1202
    s->t_attrib_default.blink = 0;
1203
    s->t_attrib_default.invers = 0;
1204
    s->t_attrib_default.unvisible = 0;
1205
    s->t_attrib_default.fgcol = COLOR_WHITE;
1206
    s->t_attrib_default.bgcol = COLOR_BLACK;
1207

    
1208
    /* set current text attributes to default */
1209
    s->t_attrib = s->t_attrib_default;
1210
    text_console_resize(s);
1211

    
1212
    qemu_chr_reset(chr);
1213

    
1214
    return chr;
1215
}