Statistics
| Branch: | Revision:

root / console.c @ 4d3b6f6e

History | View | Annotate | Download (36.6 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 "qemu-common.h"
25
#include "console.h"
26
#include "qemu-timer.h"
27

    
28
//#define DEBUG_CONSOLE
29
#define DEFAULT_BACKSCROLL 512
30
#define MAX_CONSOLES 12
31

    
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)
34

    
35
typedef 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

    
45
typedef struct TextCell {
46
    uint8_t ch;
47
    TextAttributes t_attrib;
48
} TextCell;
49

    
50
#define MAX_ESC_PARAMS 3
51

    
52
enum TTYState {
53
    TTY_STATE_NORM,
54
    TTY_STATE_ESC,
55
    TTY_STATE_CSI,
56
};
57

    
58
typedef struct QEMUFIFO {
59
    uint8_t *buf;
60
    int buf_size;
61
    int count, wptr, rptr;
62
} QEMUFIFO;
63

    
64
static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
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

    
87
static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
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

    
109
typedef enum {
110
    GRAPHIC_CONSOLE,
111
    TEXT_CONSOLE,
112
    TEXT_CONSOLE_FIXED_SIZE
113
} console_type_t;
114

    
115
/* ??? This is mis-named.
116
   It is used for both text and graphical consoles.  */
117
struct TextConsole {
118
    console_type_t console_type;
119
    DisplayState *ds;
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
    vga_hw_text_update_ptr hw_text_update;
125
    void *hw;
126

    
127
    int g_width, g_height;
128
    int width;
129
    int height;
130
    int total_height;
131
    int backscroll_height;
132
    int x, y;
133
    int x_saved, y_saved;
134
    int y_displayed;
135
    int y_base;
136
    TextAttributes t_attrib_default; /* default text attributes */
137
    TextAttributes t_attrib; /* currently active text attributes */
138
    TextCell *cells;
139
    int text_x[2], text_y[2], cursor_invalidate;
140

    
141
    enum TTYState state;
142
    int esc_params[MAX_ESC_PARAMS];
143
    int nb_esc_params;
144

    
145
    CharDriverState *chr;
146
    /* fifo for key pressed */
147
    QEMUFIFO out_fifo;
148
    uint8_t out_fifo_buf[16];
149
    QEMUTimer *kbd_timer;
150
};
151

    
152
static TextConsole *active_console;
153
static TextConsole *consoles[MAX_CONSOLES];
154
static int nb_consoles = 0;
155

    
156
void vga_hw_update(void)
157
{
158
    if (active_console && active_console->hw_update)
159
        active_console->hw_update(active_console->hw);
160
}
161

    
162
void vga_hw_invalidate(void)
163
{
164
    if (active_console->hw_invalidate)
165
        active_console->hw_invalidate(active_console->hw);
166
}
167

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

    
176
void vga_hw_text_update(console_ch_t *chardata)
177
{
178
    if (active_console && active_console->hw_text_update)
179
        active_console->hw_text_update(active_console->hw, chardata);
180
}
181

    
182
/* convert a RGBA color to a color index usable in graphic primitives */
183
static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
184
{
185
    unsigned int r, g, b, color;
186

    
187
    switch(ds->depth) {
188
#if 0
189
    case 8:
190
        r = (rgba >> 16) & 0xff;
191
        g = (rgba >> 8) & 0xff;
192
        b = (rgba) & 0xff;
193
        color = (rgb_to_index[r] * 6 * 6) +
194
            (rgb_to_index[g] * 6) +
195
            (rgb_to_index[b]);
196
        break;
197
#endif
198
    case 15:
199
        r = (rgba >> 16) & 0xff;
200
        g = (rgba >> 8) & 0xff;
201
        b = (rgba) & 0xff;
202
        color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
203
        break;
204
    case 16:
205
        r = (rgba >> 16) & 0xff;
206
        g = (rgba >> 8) & 0xff;
207
        b = (rgba) & 0xff;
208
        color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
209
        break;
210
    case 32:
211
    default:
212
        color = rgba;
213
        break;
214
    }
215
    return color;
216
}
217

    
218
static void vga_fill_rect (DisplayState *ds,
219
                           int posx, int posy, int width, int height, uint32_t color)
220
{
221
    uint8_t *d, *d1;
222
    int x, y, bpp;
223

    
224
    bpp = (ds->depth + 7) >> 3;
225
    d1 = ds->data +
226
        ds->linesize * posy + bpp * posx;
227
    for (y = 0; y < height; y++) {
228
        d = d1;
229
        switch(bpp) {
230
        case 1:
231
            for (x = 0; x < width; x++) {
232
                *((uint8_t *)d) = color;
233
                d++;
234
            }
235
            break;
236
        case 2:
237
            for (x = 0; x < width; x++) {
238
                *((uint16_t *)d) = color;
239
                d += 2;
240
            }
241
            break;
242
        case 4:
243
            for (x = 0; x < width; x++) {
244
                *((uint32_t *)d) = color;
245
                d += 4;
246
            }
247
            break;
248
        }
249
        d1 += ds->linesize;
250
    }
251
}
252

    
253
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
254
static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
255
{
256
    const uint8_t *s;
257
    uint8_t *d;
258
    int wb, y, bpp;
259

    
260
    bpp = (ds->depth + 7) >> 3;
261
    wb = w * bpp;
262
    if (yd <= ys) {
263
        s = ds->data +
264
            ds->linesize * ys + bpp * xs;
265
        d = ds->data +
266
            ds->linesize * yd + bpp * xd;
267
        for (y = 0; y < h; y++) {
268
            memmove(d, s, wb);
269
            d += ds->linesize;
270
            s += ds->linesize;
271
        }
272
    } else {
273
        s = ds->data +
274
            ds->linesize * (ys + h - 1) + bpp * xs;
275
        d = ds->data +
276
            ds->linesize * (yd + h - 1) + bpp * xd;
277
       for (y = 0; y < h; y++) {
278
            memmove(d, s, wb);
279
            d -= ds->linesize;
280
            s -= ds->linesize;
281
        }
282
    }
283
}
284

    
285
/***********************************************************/
286
/* basic char display */
287

    
288
#define FONT_HEIGHT 16
289
#define FONT_WIDTH 8
290

    
291
#include "vgafont.h"
292

    
293
#define cbswap_32(__x) \
294
((uint32_t)( \
295
                (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
296
                (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
297
                (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
298
                (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
299

    
300
#ifdef WORDS_BIGENDIAN
301
#define PAT(x) x
302
#else
303
#define PAT(x) cbswap_32(x)
304
#endif
305

    
306
static const uint32_t dmask16[16] = {
307
    PAT(0x00000000),
308
    PAT(0x000000ff),
309
    PAT(0x0000ff00),
310
    PAT(0x0000ffff),
311
    PAT(0x00ff0000),
312
    PAT(0x00ff00ff),
313
    PAT(0x00ffff00),
314
    PAT(0x00ffffff),
315
    PAT(0xff000000),
316
    PAT(0xff0000ff),
317
    PAT(0xff00ff00),
318
    PAT(0xff00ffff),
319
    PAT(0xffff0000),
320
    PAT(0xffff00ff),
321
    PAT(0xffffff00),
322
    PAT(0xffffffff),
323
};
324

    
325
static const uint32_t dmask4[4] = {
326
    PAT(0x00000000),
327
    PAT(0x0000ffff),
328
    PAT(0xffff0000),
329
    PAT(0xffffffff),
330
};
331

    
332
static uint32_t color_table[2][8];
333

    
334
enum color_names {
335
    COLOR_BLACK   = 0,
336
    COLOR_RED     = 1,
337
    COLOR_GREEN   = 2,
338
    COLOR_YELLOW  = 3,
339
    COLOR_BLUE    = 4,
340
    COLOR_MAGENTA = 5,
341
    COLOR_CYAN    = 6,
342
    COLOR_WHITE   = 7
343
};
344

    
345
static const uint32_t color_table_rgb[2][8] = {
346
    {   /* dark */
347
        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
348
        QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
349
        QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
350
        QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
351
        QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
352
        QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
353
        QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
354
        QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
355
    },
356
    {   /* bright */
357
        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
358
        QEMU_RGB(0xff, 0x00, 0x00),  /* red */
359
        QEMU_RGB(0x00, 0xff, 0x00),  /* green */
360
        QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
361
        QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
362
        QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
363
        QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
364
        QEMU_RGB(0xff, 0xff, 0xff),  /* white */
365
    }
366
};
367

    
368
static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
369
{
370
    switch(ds->depth) {
371
    case 8:
372
        col |= col << 8;
373
        col |= col << 16;
374
        break;
375
    case 15:
376
    case 16:
377
        col |= col << 16;
378
        break;
379
    default:
380
        break;
381
    }
382

    
383
    return col;
384
}
385
#ifdef DEBUG_CONSOLE
386
static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
387
{
388
    if (t_attrib->bold) {
389
        printf("b");
390
    } else {
391
        printf(" ");
392
    }
393
    if (t_attrib->uline) {
394
        printf("u");
395
    } else {
396
        printf(" ");
397
    }
398
    if (t_attrib->blink) {
399
        printf("l");
400
    } else {
401
        printf(" ");
402
    }
403
    if (t_attrib->invers) {
404
        printf("i");
405
    } else {
406
        printf(" ");
407
    }
408
    if (t_attrib->unvisible) {
409
        printf("n");
410
    } else {
411
        printf(" ");
412
    }
413

    
414
    printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
415
}
416
#endif
417

    
418
static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
419
                          TextAttributes *t_attrib)
420
{
421
    uint8_t *d;
422
    const uint8_t *font_ptr;
423
    unsigned int font_data, linesize, xorcol, bpp;
424
    int i;
425
    unsigned int fgcol, bgcol;
426

    
427
#ifdef DEBUG_CONSOLE
428
    printf("x: %2i y: %2i", x, y);
429
    console_print_text_attributes(t_attrib, ch);
430
#endif
431

    
432
    if (t_attrib->invers) {
433
        bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
434
        fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
435
    } else {
436
        fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
437
        bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
438
    }
439

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

    
494
static void text_console_resize(TextConsole *s)
495
{
496
    TextCell *cells, *c, *c1;
497
    int w1, x, y, last_width;
498

    
499
    last_width = s->width;
500
    s->width = s->g_width / FONT_WIDTH;
501
    s->height = s->g_height / FONT_HEIGHT;
502

    
503
    w1 = last_width;
504
    if (s->width < w1)
505
        w1 = s->width;
506

    
507
    cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
508
    for(y = 0; y < s->total_height; y++) {
509
        c = &cells[y * s->width];
510
        if (w1 > 0) {
511
            c1 = &s->cells[y * last_width];
512
            for(x = 0; x < w1; x++) {
513
                *c++ = *c1++;
514
            }
515
        }
516
        for(x = w1; x < s->width; x++) {
517
            c->ch = ' ';
518
            c->t_attrib = s->t_attrib_default;
519
            c++;
520
        }
521
    }
522
    qemu_free(s->cells);
523
    s->cells = cells;
524
}
525

    
526
static inline void text_update_xy(TextConsole *s, int x, int y)
527
{
528
    s->text_x[0] = MIN(s->text_x[0], x);
529
    s->text_x[1] = MAX(s->text_x[1], x);
530
    s->text_y[0] = MIN(s->text_y[0], y);
531
    s->text_y[1] = MAX(s->text_y[1], y);
532
}
533

    
534
static void update_xy(TextConsole *s, int x, int y)
535
{
536
    TextCell *c;
537
    int y1, y2;
538

    
539
    if (s == active_console) {
540
        if (!s->ds->depth) {
541
            text_update_xy(s, x, y);
542
            return;
543
        }
544

    
545
        y1 = (s->y_base + y) % s->total_height;
546
        y2 = y1 - s->y_displayed;
547
        if (y2 < 0)
548
            y2 += s->total_height;
549
        if (y2 < s->height) {
550
            c = &s->cells[y1 * s->width + x];
551
            vga_putcharxy(s->ds, x, y2, c->ch,
552
                          &(c->t_attrib));
553
            dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
554
                       FONT_WIDTH, FONT_HEIGHT);
555
        }
556
    }
557
}
558

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

    
564
    if (s == active_console) {
565
        int x = s->x;
566

    
567
        if (!s->ds->depth) {
568
            s->cursor_invalidate = 1;
569
            return;
570
        }
571

    
572
        if (x >= s->width) {
573
            x = s->width - 1;
574
        }
575
        y1 = (s->y_base + s->y) % s->total_height;
576
        y = y1 - s->y_displayed;
577
        if (y < 0)
578
            y += s->total_height;
579
        if (y < s->height) {
580
            c = &s->cells[y1 * s->width + x];
581
            if (show) {
582
                TextAttributes t_attrib = s->t_attrib_default;
583
                t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
584
                vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
585
            } else {
586
                vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
587
            }
588
            dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
589
                       FONT_WIDTH, FONT_HEIGHT);
590
        }
591
    }
592
}
593

    
594
static void console_refresh(TextConsole *s)
595
{
596
    TextCell *c;
597
    int x, y, y1;
598

    
599
    if (s != active_console)
600
        return;
601
    if (!s->ds->depth) {
602
        s->text_x[0] = 0;
603
        s->text_y[0] = 0;
604
        s->text_x[1] = s->width - 1;
605
        s->text_y[1] = s->height - 1;
606
        s->cursor_invalidate = 1;
607
        return;
608
    }
609

    
610
    vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
611
                  color_table[0][COLOR_BLACK]);
612
    y1 = s->y_displayed;
613
    for(y = 0; y < s->height; y++) {
614
        c = s->cells + y1 * s->width;
615
        for(x = 0; x < s->width; x++) {
616
            vga_putcharxy(s->ds, x, y, c->ch,
617
                          &(c->t_attrib));
618
            c++;
619
        }
620
        if (++y1 == s->total_height)
621
            y1 = 0;
622
    }
623
    dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
624
    console_show_cursor(s, 1);
625
}
626

    
627
static void console_scroll(int ydelta)
628
{
629
    TextConsole *s;
630
    int i, y1;
631

    
632
    s = active_console;
633
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
634
        return;
635

    
636
    if (ydelta > 0) {
637
        for(i = 0; i < ydelta; i++) {
638
            if (s->y_displayed == s->y_base)
639
                break;
640
            if (++s->y_displayed == s->total_height)
641
                s->y_displayed = 0;
642
        }
643
    } else {
644
        ydelta = -ydelta;
645
        i = s->backscroll_height;
646
        if (i > s->total_height - s->height)
647
            i = s->total_height - s->height;
648
        y1 = s->y_base - i;
649
        if (y1 < 0)
650
            y1 += s->total_height;
651
        for(i = 0; i < ydelta; i++) {
652
            if (s->y_displayed == y1)
653
                break;
654
            if (--s->y_displayed < 0)
655
                s->y_displayed = s->total_height - 1;
656
        }
657
    }
658
    console_refresh(s);
659
}
660

    
661
static void console_put_lf(TextConsole *s)
662
{
663
    TextCell *c;
664
    int x, y1;
665

    
666
    s->y++;
667
    if (s->y >= s->height) {
668
        s->y = s->height - 1;
669

    
670
        if (s->y_displayed == s->y_base) {
671
            if (++s->y_displayed == s->total_height)
672
                s->y_displayed = 0;
673
        }
674
        if (++s->y_base == s->total_height)
675
            s->y_base = 0;
676
        if (s->backscroll_height < s->total_height)
677
            s->backscroll_height++;
678
        y1 = (s->y_base + s->height - 1) % s->total_height;
679
        c = &s->cells[y1 * s->width];
680
        for(x = 0; x < s->width; x++) {
681
            c->ch = ' ';
682
            c->t_attrib = s->t_attrib_default;
683
            c++;
684
        }
685
        if (s == active_console && s->y_displayed == s->y_base) {
686
            if (!s->ds->depth) {
687
                s->text_x[0] = 0;
688
                s->text_y[0] = 0;
689
                s->text_x[1] = s->width - 1;
690
                s->text_y[1] = s->height - 1;
691
                return;
692
            }
693

    
694
            vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
695
                       s->width * FONT_WIDTH,
696
                       (s->height - 1) * FONT_HEIGHT);
697
            vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
698
                          s->width * FONT_WIDTH, FONT_HEIGHT,
699
                          color_table[0][s->t_attrib_default.bgcol]);
700
            dpy_update(s->ds, 0, 0,
701
                       s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
702
        }
703
    }
704
}
705

    
706
/* Set console attributes depending on the current escape codes.
707
 * NOTE: I know this code is not very efficient (checking every color for it
708
 * self) but it is more readable and better maintainable.
709
 */
710
static void console_handle_escape(TextConsole *s)
711
{
712
    int i;
713

    
714
    for (i=0; i<s->nb_esc_params; i++) {
715
        switch (s->esc_params[i]) {
716
            case 0: /* reset all console attributes to default */
717
                s->t_attrib = s->t_attrib_default;
718
                break;
719
            case 1:
720
                s->t_attrib.bold = 1;
721
                break;
722
            case 4:
723
                s->t_attrib.uline = 1;
724
                break;
725
            case 5:
726
                s->t_attrib.blink = 1;
727
                break;
728
            case 7:
729
                s->t_attrib.invers = 1;
730
                break;
731
            case 8:
732
                s->t_attrib.unvisible = 1;
733
                break;
734
            case 22:
735
                s->t_attrib.bold = 0;
736
                break;
737
            case 24:
738
                s->t_attrib.uline = 0;
739
                break;
740
            case 25:
741
                s->t_attrib.blink = 0;
742
                break;
743
            case 27:
744
                s->t_attrib.invers = 0;
745
                break;
746
            case 28:
747
                s->t_attrib.unvisible = 0;
748
                break;
749
            /* set foreground color */
750
            case 30:
751
                s->t_attrib.fgcol=COLOR_BLACK;
752
                break;
753
            case 31:
754
                s->t_attrib.fgcol=COLOR_RED;
755
                break;
756
            case 32:
757
                s->t_attrib.fgcol=COLOR_GREEN;
758
                break;
759
            case 33:
760
                s->t_attrib.fgcol=COLOR_YELLOW;
761
                break;
762
            case 34:
763
                s->t_attrib.fgcol=COLOR_BLUE;
764
                break;
765
            case 35:
766
                s->t_attrib.fgcol=COLOR_MAGENTA;
767
                break;
768
            case 36:
769
                s->t_attrib.fgcol=COLOR_CYAN;
770
                break;
771
            case 37:
772
                s->t_attrib.fgcol=COLOR_WHITE;
773
                break;
774
            /* set background color */
775
            case 40:
776
                s->t_attrib.bgcol=COLOR_BLACK;
777
                break;
778
            case 41:
779
                s->t_attrib.bgcol=COLOR_RED;
780
                break;
781
            case 42:
782
                s->t_attrib.bgcol=COLOR_GREEN;
783
                break;
784
            case 43:
785
                s->t_attrib.bgcol=COLOR_YELLOW;
786
                break;
787
            case 44:
788
                s->t_attrib.bgcol=COLOR_BLUE;
789
                break;
790
            case 45:
791
                s->t_attrib.bgcol=COLOR_MAGENTA;
792
                break;
793
            case 46:
794
                s->t_attrib.bgcol=COLOR_CYAN;
795
                break;
796
            case 47:
797
                s->t_attrib.bgcol=COLOR_WHITE;
798
                break;
799
        }
800
    }
801
}
802

    
803
static void console_clear_xy(TextConsole *s, int x, int y)
804
{
805
    int y1 = (s->y_base + y) % s->total_height;
806
    TextCell *c = &s->cells[y1 * s->width + x];
807
    c->ch = ' ';
808
    c->t_attrib = s->t_attrib_default;
809
    c++;
810
    update_xy(s, x, y);
811
}
812

    
813
static void console_putchar(TextConsole *s, int ch)
814
{
815
    TextCell *c;
816
    int y1, i;
817
    int x, y;
818

    
819
    switch(s->state) {
820
    case TTY_STATE_NORM:
821
        switch(ch) {
822
        case '\r':  /* carriage return */
823
            s->x = 0;
824
            break;
825
        case '\n':  /* newline */
826
            console_put_lf(s);
827
            break;
828
        case '\b':  /* backspace */
829
            if (s->x > 0)
830
                s->x--;
831
            break;
832
        case '\t':  /* tabspace */
833
            if (s->x + (8 - (s->x % 8)) > s->width) {
834
                s->x = 0;
835
                console_put_lf(s);
836
            } else {
837
                s->x = s->x + (8 - (s->x % 8));
838
            }
839
            break;
840
        case '\a':  /* alert aka. bell */
841
            /* TODO: has to be implemented */
842
            break;
843
        case 14:
844
            /* SI (shift in), character set 0 (ignored) */
845
            break;
846
        case 15:
847
            /* SO (shift out), character set 1 (ignored) */
848
            break;
849
        case 27:    /* esc (introducing an escape sequence) */
850
            s->state = TTY_STATE_ESC;
851
            break;
852
        default:
853
            if (s->x >= s->width) {
854
                /* line wrap */
855
                s->x = 0;
856
                console_put_lf(s);
857
            }
858
            y1 = (s->y_base + s->y) % s->total_height;
859
            c = &s->cells[y1 * s->width + s->x];
860
            c->ch = ch;
861
            c->t_attrib = s->t_attrib;
862
            update_xy(s, s->x, s->y);
863
            s->x++;
864
            break;
865
        }
866
        break;
867
    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
868
        if (ch == '[') {
869
            for(i=0;i<MAX_ESC_PARAMS;i++)
870
                s->esc_params[i] = 0;
871
            s->nb_esc_params = 0;
872
            s->state = TTY_STATE_CSI;
873
        } else {
874
            s->state = TTY_STATE_NORM;
875
        }
876
        break;
877
    case TTY_STATE_CSI: /* handle escape sequence parameters */
878
        if (ch >= '0' && ch <= '9') {
879
            if (s->nb_esc_params < MAX_ESC_PARAMS) {
880
                s->esc_params[s->nb_esc_params] =
881
                    s->esc_params[s->nb_esc_params] * 10 + ch - '0';
882
            }
883
        } else {
884
            s->nb_esc_params++;
885
            if (ch == ';')
886
                break;
887
#ifdef DEBUG_CONSOLE
888
            fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
889
                    s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
890
#endif
891
            s->state = TTY_STATE_NORM;
892
            switch(ch) {
893
            case 'A':
894
                /* move cursor up */
895
                if (s->esc_params[0] == 0) {
896
                    s->esc_params[0] = 1;
897
                }
898
                s->y -= s->esc_params[0];
899
                if (s->y < 0) {
900
                    s->y = 0;
901
                }
902
                break;
903
            case 'B':
904
                /* move cursor down */
905
                if (s->esc_params[0] == 0) {
906
                    s->esc_params[0] = 1;
907
                }
908
                s->y += s->esc_params[0];
909
                if (s->y >= s->height) {
910
                    s->y = s->height - 1;
911
                }
912
                break;
913
            case 'C':
914
                /* move cursor right */
915
                if (s->esc_params[0] == 0) {
916
                    s->esc_params[0] = 1;
917
                }
918
                s->x += s->esc_params[0];
919
                if (s->x >= s->width) {
920
                    s->x = s->width - 1;
921
                }
922
                break;
923
            case 'D':
924
                /* move cursor left */
925
                if (s->esc_params[0] == 0) {
926
                    s->esc_params[0] = 1;
927
                }
928
                s->x -= s->esc_params[0];
929
                if (s->x < 0) {
930
                    s->x = 0;
931
                }
932
                break;
933
            case 'G':
934
                /* move cursor to column */
935
                s->x = s->esc_params[0] - 1;
936
                if (s->x < 0) {
937
                    s->x = 0;
938
                }
939
                break;
940
            case 'f':
941
            case 'H':
942
                /* move cursor to row, column */
943
                s->x = s->esc_params[1] - 1;
944
                if (s->x < 0) {
945
                    s->x = 0;
946
                }
947
                s->y = s->esc_params[0] - 1;
948
                if (s->y < 0) {
949
                    s->y = 0;
950
                }
951
                break;
952
            case 'J':
953
                switch (s->esc_params[0]) {
954
                case 0:
955
                    /* clear to end of screen */
956
                    for (y = s->y; y < s->height; y++) {
957
                        for (x = 0; x < s->width; x++) {
958
                            if (y == s->y && x < s->x) {
959
                                continue;
960
                            }
961
                            console_clear_xy(s, x, y);
962
                        }
963
                    }
964
                    break;
965
                case 1:
966
                    /* clear from beginning of screen */
967
                    for (y = 0; y <= s->y; y++) {
968
                        for (x = 0; x < s->width; x++) {
969
                            if (y == s->y && x > s->x) {
970
                                break;
971
                            }
972
                            console_clear_xy(s, x, y);
973
                        }
974
                    }
975
                    break;
976
                case 2:
977
                    /* clear entire screen */
978
                    for (y = 0; y <= s->height; y++) {
979
                        for (x = 0; x < s->width; x++) {
980
                            console_clear_xy(s, x, y);
981
                        }
982
                    }
983
                break;
984
                }
985
            case 'K':
986
                switch (s->esc_params[0]) {
987
                case 0:
988
                /* clear to eol */
989
                for(x = s->x; x < s->width; x++) {
990
                        console_clear_xy(s, x, s->y);
991
                }
992
                break;
993
                case 1:
994
                    /* clear from beginning of line */
995
                    for (x = 0; x <= s->x; x++) {
996
                        console_clear_xy(s, x, s->y);
997
                    }
998
                    break;
999
                case 2:
1000
                    /* clear entire line */
1001
                    for(x = 0; x < s->width; x++) {
1002
                        console_clear_xy(s, x, s->y);
1003
                    }
1004
                break;
1005
            }
1006
                break;
1007
            case 'm':
1008
            console_handle_escape(s);
1009
            break;
1010
            case 'n':
1011
                /* report cursor position */
1012
                /* TODO: send ESC[row;colR */
1013
                break;
1014
            case 's':
1015
                /* save cursor position */
1016
                s->x_saved = s->x;
1017
                s->y_saved = s->y;
1018
                break;
1019
            case 'u':
1020
                /* restore cursor position */
1021
                s->x = s->x_saved;
1022
                s->y = s->y_saved;
1023
                break;
1024
            default:
1025
#ifdef DEBUG_CONSOLE
1026
                fprintf(stderr, "unhandled escape character '%c'\n", ch);
1027
#endif
1028
                break;
1029
            }
1030
            break;
1031
        }
1032
    }
1033
}
1034

    
1035
void console_select(unsigned int index)
1036
{
1037
    TextConsole *s;
1038

    
1039
    if (index >= MAX_CONSOLES)
1040
        return;
1041
    s = consoles[index];
1042
    if (s) {
1043
        active_console = s;
1044
        vga_hw_invalidate();
1045
    }
1046
}
1047

    
1048
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1049
{
1050
    TextConsole *s = chr->opaque;
1051
    int i;
1052

    
1053
    console_show_cursor(s, 0);
1054
    for(i = 0; i < len; i++) {
1055
        console_putchar(s, buf[i]);
1056
    }
1057
    console_show_cursor(s, 1);
1058
    return len;
1059
}
1060

    
1061
static void console_send_event(CharDriverState *chr, int event)
1062
{
1063
    TextConsole *s = chr->opaque;
1064
    int i;
1065

    
1066
    if (event == CHR_EVENT_FOCUS) {
1067
        for(i = 0; i < nb_consoles; i++) {
1068
            if (consoles[i] == s) {
1069
                console_select(i);
1070
                break;
1071
            }
1072
        }
1073
    }
1074
}
1075

    
1076
static void kbd_send_chars(void *opaque)
1077
{
1078
    TextConsole *s = opaque;
1079
    int len;
1080
    uint8_t buf[16];
1081

    
1082
    len = qemu_chr_can_read(s->chr);
1083
    if (len > s->out_fifo.count)
1084
        len = s->out_fifo.count;
1085
    if (len > 0) {
1086
        if (len > sizeof(buf))
1087
            len = sizeof(buf);
1088
        qemu_fifo_read(&s->out_fifo, buf, len);
1089
        qemu_chr_read(s->chr, buf, len);
1090
    }
1091
    /* characters are pending: we send them a bit later (XXX:
1092
       horrible, should change char device API) */
1093
    if (s->out_fifo.count > 0) {
1094
        qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1095
    }
1096
}
1097

    
1098
/* called when an ascii key is pressed */
1099
void kbd_put_keysym(int keysym)
1100
{
1101
    TextConsole *s;
1102
    uint8_t buf[16], *q;
1103
    int c;
1104

    
1105
    s = active_console;
1106
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
1107
        return;
1108

    
1109
    switch(keysym) {
1110
    case QEMU_KEY_CTRL_UP:
1111
        console_scroll(-1);
1112
        break;
1113
    case QEMU_KEY_CTRL_DOWN:
1114
        console_scroll(1);
1115
        break;
1116
    case QEMU_KEY_CTRL_PAGEUP:
1117
        console_scroll(-10);
1118
        break;
1119
    case QEMU_KEY_CTRL_PAGEDOWN:
1120
        console_scroll(10);
1121
        break;
1122
    default:
1123
        /* convert the QEMU keysym to VT100 key string */
1124
        q = buf;
1125
        if (keysym >= 0xe100 && keysym <= 0xe11f) {
1126
            *q++ = '\033';
1127
            *q++ = '[';
1128
            c = keysym - 0xe100;
1129
            if (c >= 10)
1130
                *q++ = '0' + (c / 10);
1131
            *q++ = '0' + (c % 10);
1132
            *q++ = '~';
1133
        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1134
            *q++ = '\033';
1135
            *q++ = '[';
1136
            *q++ = keysym & 0xff;
1137
        } else {
1138
                *q++ = keysym;
1139
        }
1140
        if (s->chr->chr_read) {
1141
            qemu_fifo_write(&s->out_fifo, buf, q - buf);
1142
            kbd_send_chars(s);
1143
        }
1144
        break;
1145
    }
1146
}
1147

    
1148
static void text_console_invalidate(void *opaque)
1149
{
1150
    TextConsole *s = (TextConsole *) opaque;
1151

    
1152
    if (s->console_type != GRAPHIC_CONSOLE) {
1153
        if (s->g_width != s->ds->width ||
1154
            s->g_height != s->ds->height) {
1155
            if (s->console_type == TEXT_CONSOLE_FIXED_SIZE)
1156
                dpy_resize(s->ds, s->g_width, s->g_height);
1157
            else {
1158
                s->g_width = s->ds->width;
1159
                s->g_height = s->ds->height;
1160
                text_console_resize(s);
1161
            }
1162
        }
1163
    }
1164
    console_refresh(s);
1165
}
1166

    
1167
static void text_console_update(void *opaque, console_ch_t *chardata)
1168
{
1169
    TextConsole *s = (TextConsole *) opaque;
1170
    int i, j, src;
1171

    
1172
    if (s->text_x[0] <= s->text_x[1]) {
1173
        src = (s->y_base + s->text_y[0]) * s->width;
1174
        chardata += s->text_y[0] * s->width;
1175
        for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1176
            for (j = 0; j < s->width; j ++, src ++)
1177
                console_write_ch(chardata ++, s->cells[src].ch |
1178
                                (s->cells[src].t_attrib.fgcol << 12) |
1179
                                (s->cells[src].t_attrib.bgcol << 8) |
1180
                                (s->cells[src].t_attrib.bold << 21));
1181
        dpy_update(s->ds, s->text_x[0], s->text_y[0],
1182
                   s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1183
        s->text_x[0] = s->width;
1184
        s->text_y[0] = s->height;
1185
        s->text_x[1] = 0;
1186
        s->text_y[1] = 0;
1187
    }
1188
    if (s->cursor_invalidate) {
1189
        dpy_cursor(s->ds, s->x, s->y);
1190
        s->cursor_invalidate = 0;
1191
    }
1192
}
1193

    
1194
static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1195
{
1196
    TextConsole *s;
1197
    int i;
1198

    
1199
    if (nb_consoles >= MAX_CONSOLES)
1200
        return NULL;
1201
    s = qemu_mallocz(sizeof(TextConsole));
1202
    if (!s) {
1203
        return NULL;
1204
    }
1205
    if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1206
        (console_type == GRAPHIC_CONSOLE))) {
1207
        active_console = s;
1208
    }
1209
    s->ds = ds;
1210
    s->console_type = console_type;
1211
    if (console_type != GRAPHIC_CONSOLE) {
1212
        consoles[nb_consoles++] = s;
1213
    } else {
1214
        /* HACK: Put graphical consoles before text consoles.  */
1215
        for (i = nb_consoles; i > 0; i--) {
1216
            if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1217
                break;
1218
            consoles[i] = consoles[i - 1];
1219
        }
1220
        consoles[i] = s;
1221
    }
1222
    return s;
1223
}
1224

    
1225
TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1226
                                  vga_hw_invalidate_ptr invalidate,
1227
                                  vga_hw_screen_dump_ptr screen_dump,
1228
                                  vga_hw_text_update_ptr text_update,
1229
                                  void *opaque)
1230
{
1231
    TextConsole *s;
1232

    
1233
    s = new_console(ds, GRAPHIC_CONSOLE);
1234
    if (!s)
1235
      return NULL;
1236
    s->hw_update = update;
1237
    s->hw_invalidate = invalidate;
1238
    s->hw_screen_dump = screen_dump;
1239
    s->hw_text_update = text_update;
1240
    s->hw = opaque;
1241
    return s;
1242
}
1243

    
1244
int is_graphic_console(void)
1245
{
1246
    return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1247
}
1248

    
1249
void console_color_init(DisplayState *ds)
1250
{
1251
    int i, j;
1252
    for (j = 0; j < 2; j++) {
1253
        for (i = 0; i < 8; i++) {
1254
            color_table[j][i] = col_expand(ds, 
1255
                   vga_get_color(ds, color_table_rgb[j][i]));
1256
        }
1257
    }
1258
}
1259

    
1260
CharDriverState *text_console_init(DisplayState *ds, const char *p)
1261
{
1262
    CharDriverState *chr;
1263
    TextConsole *s;
1264
    unsigned width;
1265
    unsigned height;
1266
    static int color_inited;
1267

    
1268
    chr = qemu_mallocz(sizeof(CharDriverState));
1269
    if (!chr)
1270
        return NULL;
1271
    s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
1272
    if (!s) {
1273
        free(chr);
1274
        return NULL;
1275
    }
1276
    chr->opaque = s;
1277
    chr->chr_write = console_puts;
1278
    chr->chr_send_event = console_send_event;
1279

    
1280
    s->chr = chr;
1281
    s->out_fifo.buf = s->out_fifo_buf;
1282
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1283
    s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1284

    
1285
    if (!color_inited) {
1286
        color_inited = 1;
1287
        console_color_init(s->ds);
1288
    }
1289
    s->y_displayed = 0;
1290
    s->y_base = 0;
1291
    s->total_height = DEFAULT_BACKSCROLL;
1292
    s->x = 0;
1293
    s->y = 0;
1294
    width = s->ds->width;
1295
    height = s->ds->height;
1296
    if (p != 0) {
1297
        width = strtoul(p, (char **)&p, 10);
1298
        if (*p == 'C') {
1299
            p++;
1300
            width *= FONT_WIDTH;
1301
        }
1302
        if (*p == 'x') {
1303
            p++;
1304
            height = strtoul(p, (char **)&p, 10);
1305
            if (*p == 'C') {
1306
                p++;
1307
                height *= FONT_HEIGHT;
1308
            }
1309
        }
1310
    }
1311
    s->g_width = width;
1312
    s->g_height = height;
1313

    
1314
    s->hw_invalidate = text_console_invalidate;
1315
    s->hw_text_update = text_console_update;
1316
    s->hw = s;
1317

    
1318
    /* Set text attribute defaults */
1319
    s->t_attrib_default.bold = 0;
1320
    s->t_attrib_default.uline = 0;
1321
    s->t_attrib_default.blink = 0;
1322
    s->t_attrib_default.invers = 0;
1323
    s->t_attrib_default.unvisible = 0;
1324
    s->t_attrib_default.fgcol = COLOR_WHITE;
1325
    s->t_attrib_default.bgcol = COLOR_BLACK;
1326

    
1327
    /* set current text attributes to default */
1328
    s->t_attrib = s->t_attrib_default;
1329
    text_console_resize(s);
1330

    
1331
    qemu_chr_reset(chr);
1332

    
1333
    return chr;
1334
}