Statistics
| Branch: | Revision:

root / console.c @ 7b455225

History | View | Annotate | Download (36.9 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
#define DEFAULT_MONITOR_SIZE "800x600"
32

    
33
#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
34
#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
35

    
36
typedef struct TextAttributes {
37
    uint8_t fgcol:4;
38
    uint8_t bgcol:4;
39
    uint8_t bold:1;
40
    uint8_t uline:1;
41
    uint8_t blink:1;
42
    uint8_t invers:1;
43
    uint8_t unvisible:1;
44
} TextAttributes;
45

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

    
51
#define MAX_ESC_PARAMS 3
52

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

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

    
65
static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
66
{
67
    int l, len;
68

    
69
    l = f->buf_size - f->count;
70
    if (len1 > l)
71
        len1 = l;
72
    len = len1;
73
    while (len > 0) {
74
        l = f->buf_size - f->wptr;
75
        if (l > len)
76
            l = len;
77
        memcpy(f->buf + f->wptr, buf, l);
78
        f->wptr += l;
79
        if (f->wptr >= f->buf_size)
80
            f->wptr = 0;
81
        buf += l;
82
        len -= l;
83
    }
84
    f->count += len1;
85
    return len1;
86
}
87

    
88
static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
89
{
90
    int l, len;
91

    
92
    if (len1 > f->count)
93
        len1 = f->count;
94
    len = len1;
95
    while (len > 0) {
96
        l = f->buf_size - f->rptr;
97
        if (l > len)
98
            l = len;
99
        memcpy(buf, f->buf + f->rptr, l);
100
        f->rptr += l;
101
        if (f->rptr >= f->buf_size)
102
            f->rptr = 0;
103
        buf += l;
104
        len -= l;
105
    }
106
    f->count -= len1;
107
    return len1;
108
}
109

    
110
typedef enum {
111
    GRAPHIC_CONSOLE,
112
    TEXT_CONSOLE
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
    TextConsole *previous_active_console;
171

    
172
    previous_active_console = active_console;
173
    active_console = consoles[0];
174
    /* There is currently no way of specifying which screen we want to dump,
175
       so always dump the first one.  */
176
    if (consoles[0]->hw_screen_dump)
177
        consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
178
    active_console = previous_active_console;
179
}
180

    
181
void vga_hw_text_update(console_ch_t *chardata)
182
{
183
    if (active_console && active_console->hw_text_update)
184
        active_console->hw_text_update(active_console->hw, chardata);
185
}
186

    
187
/* convert a RGBA color to a color index usable in graphic primitives */
188
static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
189
{
190
    unsigned int r, g, b, color;
191

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

    
223
static void vga_fill_rect (DisplayState *ds,
224
                           int posx, int posy, int width, int height, uint32_t color)
225
{
226
    uint8_t *d, *d1;
227
    int x, y, bpp;
228

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

    
258
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
259
static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
260
{
261
    const uint8_t *s;
262
    uint8_t *d;
263
    int wb, y, bpp;
264

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

    
290
/***********************************************************/
291
/* basic char display */
292

    
293
#define FONT_HEIGHT 16
294
#define FONT_WIDTH 8
295

    
296
#include "vgafont.h"
297

    
298
#define cbswap_32(__x) \
299
((uint32_t)( \
300
                (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
301
                (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
302
                (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
303
                (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
304

    
305
#ifdef WORDS_BIGENDIAN
306
#define PAT(x) x
307
#else
308
#define PAT(x) cbswap_32(x)
309
#endif
310

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

    
330
static const uint32_t dmask4[4] = {
331
    PAT(0x00000000),
332
    PAT(0x0000ffff),
333
    PAT(0xffff0000),
334
    PAT(0xffffffff),
335
};
336

    
337
static uint32_t color_table[2][8];
338

    
339
enum color_names {
340
    COLOR_BLACK   = 0,
341
    COLOR_RED     = 1,
342
    COLOR_GREEN   = 2,
343
    COLOR_YELLOW  = 3,
344
    COLOR_BLUE    = 4,
345
    COLOR_MAGENTA = 5,
346
    COLOR_CYAN    = 6,
347
    COLOR_WHITE   = 7
348
};
349

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

    
373
static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
374
{
375
    switch(ds->depth) {
376
    case 8:
377
        col |= col << 8;
378
        col |= col << 16;
379
        break;
380
    case 15:
381
    case 16:
382
        col |= col << 16;
383
        break;
384
    default:
385
        break;
386
    }
387

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

    
419
    printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
420
}
421
#endif
422

    
423
static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
424
                          TextAttributes *t_attrib)
425
{
426
    uint8_t *d;
427
    const uint8_t *font_ptr;
428
    unsigned int font_data, linesize, xorcol, bpp;
429
    int i;
430
    unsigned int fgcol, bgcol;
431

    
432
#ifdef DEBUG_CONSOLE
433
    printf("x: %2i y: %2i", x, y);
434
    console_print_text_attributes(t_attrib, ch);
435
#endif
436

    
437
    if (t_attrib->invers) {
438
        bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
439
        fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
440
    } else {
441
        fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
442
        bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
443
    }
444

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

    
499
static void text_console_resize(TextConsole *s)
500
{
501
    TextCell *cells, *c, *c1;
502
    int w1, x, y, last_width;
503

    
504
    last_width = s->width;
505
    s->width = s->g_width / FONT_WIDTH;
506
    s->height = s->g_height / FONT_HEIGHT;
507

    
508
    w1 = last_width;
509
    if (s->width < w1)
510
        w1 = s->width;
511

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

    
531
static inline void text_update_xy(TextConsole *s, int x, int y)
532
{
533
    s->text_x[0] = MIN(s->text_x[0], x);
534
    s->text_x[1] = MAX(s->text_x[1], x);
535
    s->text_y[0] = MIN(s->text_y[0], y);
536
    s->text_y[1] = MAX(s->text_y[1], y);
537
}
538

    
539
static void update_xy(TextConsole *s, int x, int y)
540
{
541
    TextCell *c;
542
    int y1, y2;
543

    
544
    if (s == active_console) {
545
        if (!s->ds->depth) {
546
            text_update_xy(s, x, y);
547
            return;
548
        }
549

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

    
564
static void console_show_cursor(TextConsole *s, int show)
565
{
566
    TextCell *c;
567
    int y, y1;
568

    
569
    if (s == active_console) {
570
        int x = s->x;
571

    
572
        if (!s->ds->depth) {
573
            s->cursor_invalidate = 1;
574
            return;
575
        }
576

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

    
599
static void console_refresh(TextConsole *s)
600
{
601
    TextCell *c;
602
    int x, y, y1;
603

    
604
    if (s != active_console)
605
        return;
606
    if (!s->ds->depth) {
607
        s->text_x[0] = 0;
608
        s->text_y[0] = 0;
609
        s->text_x[1] = s->width - 1;
610
        s->text_y[1] = s->height - 1;
611
        s->cursor_invalidate = 1;
612
        return;
613
    }
614

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

    
632
static void console_scroll(int ydelta)
633
{
634
    TextConsole *s;
635
    int i, y1;
636

    
637
    s = active_console;
638
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
639
        return;
640

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

    
666
static void console_put_lf(TextConsole *s)
667
{
668
    TextCell *c;
669
    int x, y1;
670

    
671
    s->y++;
672
    if (s->y >= s->height) {
673
        s->y = s->height - 1;
674

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

    
699
            vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
700
                       s->width * FONT_WIDTH,
701
                       (s->height - 1) * FONT_HEIGHT);
702
            vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
703
                          s->width * FONT_WIDTH, FONT_HEIGHT,
704
                          color_table[0][s->t_attrib_default.bgcol]);
705
            dpy_update(s->ds, 0, 0,
706
                       s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
707
        }
708
    }
709
}
710

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

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

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

    
818
static void console_putchar(TextConsole *s, int ch)
819
{
820
    TextCell *c;
821
    int y1, i;
822
    int x, y;
823

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

    
1040
void console_select(unsigned int index)
1041
{
1042
    TextConsole *s;
1043

    
1044
    if (index >= MAX_CONSOLES)
1045
        return;
1046
    s = consoles[index];
1047
    if (s) {
1048
        active_console = s;
1049
        if (s->g_width && s->g_height
1050
            && (s->g_width != s->ds->width || s->g_height != s->ds->height))
1051
            dpy_resize(s->ds, s->g_width, s->g_height);
1052
        vga_hw_invalidate();
1053
    }
1054
}
1055

    
1056
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1057
{
1058
    TextConsole *s = chr->opaque;
1059
    int i;
1060

    
1061
    console_show_cursor(s, 0);
1062
    for(i = 0; i < len; i++) {
1063
        console_putchar(s, buf[i]);
1064
    }
1065
    console_show_cursor(s, 1);
1066
    return len;
1067
}
1068

    
1069
static void console_send_event(CharDriverState *chr, int event)
1070
{
1071
    TextConsole *s = chr->opaque;
1072
    int i;
1073

    
1074
    if (event == CHR_EVENT_FOCUS) {
1075
        for(i = 0; i < nb_consoles; i++) {
1076
            if (consoles[i] == s) {
1077
                console_select(i);
1078
                break;
1079
            }
1080
        }
1081
    }
1082
}
1083

    
1084
static void kbd_send_chars(void *opaque)
1085
{
1086
    TextConsole *s = opaque;
1087
    int len;
1088
    uint8_t buf[16];
1089

    
1090
    len = qemu_chr_can_read(s->chr);
1091
    if (len > s->out_fifo.count)
1092
        len = s->out_fifo.count;
1093
    if (len > 0) {
1094
        if (len > sizeof(buf))
1095
            len = sizeof(buf);
1096
        qemu_fifo_read(&s->out_fifo, buf, len);
1097
        qemu_chr_read(s->chr, buf, len);
1098
    }
1099
    /* characters are pending: we send them a bit later (XXX:
1100
       horrible, should change char device API) */
1101
    if (s->out_fifo.count > 0) {
1102
        qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1103
    }
1104
}
1105

    
1106
/* called when an ascii key is pressed */
1107
void kbd_put_keysym(int keysym)
1108
{
1109
    TextConsole *s;
1110
    uint8_t buf[16], *q;
1111
    int c;
1112

    
1113
    s = active_console;
1114
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
1115
        return;
1116

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

    
1156
static void text_console_invalidate(void *opaque)
1157
{
1158
    TextConsole *s = (TextConsole *) opaque;
1159

    
1160
    console_refresh(s);
1161
}
1162

    
1163
static void text_console_update(void *opaque, console_ch_t *chardata)
1164
{
1165
    TextConsole *s = (TextConsole *) opaque;
1166
    int i, j, src;
1167

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

    
1190
static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1191
{
1192
    TextConsole *s;
1193
    int i;
1194

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

    
1221
TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1222
                                  vga_hw_invalidate_ptr invalidate,
1223
                                  vga_hw_screen_dump_ptr screen_dump,
1224
                                  vga_hw_text_update_ptr text_update,
1225
                                  void *opaque)
1226
{
1227
    TextConsole *s;
1228

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

    
1240
int is_graphic_console(void)
1241
{
1242
    return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1243
}
1244

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

    
1256
CharDriverState *text_console_init(DisplayState *ds, const char *p)
1257
{
1258
    CharDriverState *chr;
1259
    TextConsole *s;
1260
    unsigned width;
1261
    unsigned height;
1262
    static int color_inited;
1263

    
1264
    chr = qemu_mallocz(sizeof(CharDriverState));
1265
    if (!chr)
1266
        return NULL;
1267
    s = new_console(ds, TEXT_CONSOLE);
1268
    if (!s) {
1269
        free(chr);
1270
        return NULL;
1271
    }
1272
    if (!p)
1273
        p = DEFAULT_MONITOR_SIZE;
1274

    
1275
    chr->opaque = s;
1276
    chr->chr_write = console_puts;
1277
    chr->chr_send_event = console_send_event;
1278

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

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

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

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

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

    
1330
    qemu_chr_reset(chr);
1331

    
1332
    return chr;
1333
}
1334

    
1335
void qemu_console_resize(QEMUConsole *console, int width, int height)
1336
{
1337
    if (console->g_width != width || console->g_height != height
1338
        || !console->ds->data) {
1339
        console->g_width = width;
1340
        console->g_height = height;
1341
        if (active_console == console) {
1342
            dpy_resize(console->ds, width, height);
1343
        }
1344
    }
1345
}