Statistics
| Branch: | Revision:

root / console.c @ ba3c64fb

History | View | Annotate | Download (19.1 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 DEFAULT_BACKSCROLL 512
27
#define MAX_CONSOLES 12
28

    
29
#define RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
30
#define RGB(r, g, b) RGBA(r, g, b, 0xff)
31

    
32
typedef struct TextCell {
33
    uint8_t ch;
34
    uint8_t bgcol:4;
35
    uint8_t fgcol:4;
36
} TextCell;
37

    
38
#define MAX_ESC_PARAMS 3
39

    
40
enum TTYState {
41
    TTY_STATE_NORM,
42
    TTY_STATE_ESC,
43
    TTY_STATE_CSI,
44
};
45

    
46
struct TextConsole {
47
    int text_console; /* true if text console */
48
    DisplayState *ds;
49
    int g_width, g_height;
50
    int width;
51
    int height;
52
    int total_height;
53
    int backscroll_height;
54
    int fgcol;
55
    int bgcol;
56
    int x, y;
57
    int y_displayed;
58
    int y_base;
59
    TextCell *cells;
60

    
61
    enum TTYState state;
62
    int esc_params[MAX_ESC_PARAMS];
63
    int nb_esc_params;
64

    
65
    /* kbd read handler */
66
    IOReadHandler *fd_read;
67
    void *fd_opaque;
68
};
69

    
70
static TextConsole *active_console;
71
static TextConsole *consoles[MAX_CONSOLES];
72
static int nb_consoles = 0;
73

    
74
/* convert a RGBA color to a color index usable in graphic primitives */
75
static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
76
{
77
    unsigned int r, g, b, color;
78

    
79
    switch(ds->depth) {
80
#if 0
81
    case 8:
82
        r = (rgba >> 16) & 0xff;
83
        g = (rgba >> 8) & 0xff;
84
        b = (rgba) & 0xff;
85
        color = (rgb_to_index[r] * 6 * 6) + 
86
            (rgb_to_index[g] * 6) + 
87
            (rgb_to_index[b]);
88
        break;
89
#endif
90
    case 15:
91
        r = (rgba >> 16) & 0xff;
92
        g = (rgba >> 8) & 0xff;
93
        b = (rgba) & 0xff;
94
        color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
95
        break;
96
    case 16:
97
        r = (rgba >> 16) & 0xff;
98
        g = (rgba >> 8) & 0xff;
99
        b = (rgba) & 0xff;
100
        color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
101
        break;
102
    case 32:
103
    default:
104
        color = rgba;
105
        break;
106
    }
107
    return color;
108
}
109

    
110
static void vga_fill_rect (DisplayState *ds, 
111
                           int posx, int posy, int width, int height, uint32_t color)
112
{
113
    uint8_t *d, *d1;
114
    int x, y, bpp;
115
    
116
    bpp = (ds->depth + 7) >> 3;
117
    d1 = ds->data + 
118
        ds->linesize * posy + bpp * posx;
119
    for (y = 0; y < height; y++) {
120
        d = d1;
121
        switch(bpp) {
122
        case 1:
123
            for (x = 0; x < width; x++) {
124
                *((uint8_t *)d) = color;
125
                d++;
126
            }
127
            break;
128
        case 2:
129
            for (x = 0; x < width; x++) {
130
                *((uint16_t *)d) = color;
131
                d += 2;
132
            }
133
            break;
134
        case 4:
135
            for (x = 0; x < width; x++) {
136
                *((uint32_t *)d) = color;
137
                d += 4;
138
            }
139
            break;
140
        }
141
        d1 += ds->linesize;
142
    }
143
}
144

    
145
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
146
static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
147
{
148
    const uint8_t *s;
149
    uint8_t *d;
150
    int wb, y, bpp;
151

    
152
    bpp = (ds->depth + 7) >> 3;
153
    wb = w * bpp;
154
    if (yd <= ys) {
155
        s = ds->data + 
156
            ds->linesize * ys + bpp * xs;
157
        d = ds->data + 
158
            ds->linesize * yd + bpp * xd;
159
        for (y = 0; y < h; y++) {
160
            memmove(d, s, wb);
161
            d += ds->linesize;
162
            s += ds->linesize;
163
        }
164
    } else {
165
        s = ds->data + 
166
            ds->linesize * (ys + h - 1) + bpp * xs;
167
        d = ds->data + 
168
            ds->linesize * (yd + h - 1) + bpp * xd;
169
       for (y = 0; y < h; y++) {
170
            memmove(d, s, wb);
171
            d -= ds->linesize;
172
            s -= ds->linesize;
173
        }
174
    }
175
}
176

    
177
/***********************************************************/
178
/* basic char display */
179

    
180
#define FONT_HEIGHT 16
181
#define FONT_WIDTH 8
182

    
183
#include "vgafont.h"
184

    
185
#define cbswap_32(__x) \
186
((uint32_t)( \
187
                (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
188
                (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
189
                (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
190
                (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
191

    
192
#ifdef WORDS_BIGENDIAN
193
#define PAT(x) x
194
#else
195
#define PAT(x) cbswap_32(x)
196
#endif
197

    
198
static const uint32_t dmask16[16] = {
199
    PAT(0x00000000),
200
    PAT(0x000000ff),
201
    PAT(0x0000ff00),
202
    PAT(0x0000ffff),
203
    PAT(0x00ff0000),
204
    PAT(0x00ff00ff),
205
    PAT(0x00ffff00),
206
    PAT(0x00ffffff),
207
    PAT(0xff000000),
208
    PAT(0xff0000ff),
209
    PAT(0xff00ff00),
210
    PAT(0xff00ffff),
211
    PAT(0xffff0000),
212
    PAT(0xffff00ff),
213
    PAT(0xffffff00),
214
    PAT(0xffffffff),
215
};
216

    
217
static const uint32_t dmask4[4] = {
218
    PAT(0x00000000),
219
    PAT(0x0000ffff),
220
    PAT(0xffff0000),
221
    PAT(0xffffffff),
222
};
223

    
224
static uint32_t color_table[8];
225

    
226
static const uint32_t color_table_rgb[8] = {
227
    RGB(0x00, 0x00, 0x00),
228
    RGB(0xff, 0x00, 0x00),
229
    RGB(0x00, 0xff, 0x00),
230
    RGB(0xff, 0xff, 0x00),
231
    RGB(0x00, 0x00, 0xff),
232
    RGB(0xff, 0x00, 0xff),
233
    RGB(0x00, 0xff, 0xff),
234
    RGB(0xff, 0xff, 0xff),
235
};
236

    
237
static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
238
{
239
    switch(ds->depth) {
240
    case 8:
241
        col |= col << 8;
242
        col |= col << 16;
243
        break;
244
    case 15:
245
    case 16:
246
        col |= col << 16;
247
        break;
248
    default:
249
        break;
250
    }
251

    
252
    return col;
253
}
254

    
255
static void vga_putcharxy(DisplayState *ds, int x, int y, int ch, 
256
                          unsigned int fgcol, unsigned int bgcol)
257
{
258
    uint8_t *d;
259
    const uint8_t *font_ptr;
260
    unsigned int font_data, linesize, xorcol, bpp;
261
    int i;
262

    
263
    bpp = (ds->depth + 7) >> 3;
264
    d = ds->data + 
265
        ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
266
    linesize = ds->linesize;
267
    font_ptr = vgafont16 + FONT_HEIGHT * ch;
268
    xorcol = bgcol ^ fgcol;
269
    switch(ds->depth) {
270
    case 8:
271
        for(i = 0; i < FONT_HEIGHT; i++) {
272
            font_data = *font_ptr++;
273
            ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
274
            ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
275
            d += linesize;
276
        }
277
        break;
278
    case 16:
279
    case 15:
280
        for(i = 0; i < FONT_HEIGHT; i++) {
281
            font_data = *font_ptr++;
282
            ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
283
            ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
284
            ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
285
            ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
286
            d += linesize;
287
        }
288
        break;
289
    case 32:
290
        for(i = 0; i < FONT_HEIGHT; i++) {
291
            font_data = *font_ptr++;
292
            ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
293
            ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
294
            ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
295
            ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
296
            ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
297
            ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
298
            ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
299
            ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
300
            d += linesize;
301
        }
302
        break;
303
    }
304
}
305

    
306
static void text_console_resize(TextConsole *s)
307
{
308
    TextCell *cells, *c, *c1;
309
    int w1, x, y, last_width;
310

    
311
    last_width = s->width;
312
    s->width = s->g_width / FONT_WIDTH;
313
    s->height = s->g_height / FONT_HEIGHT;
314

    
315
    w1 = last_width;
316
    if (s->width < w1)
317
        w1 = s->width;
318

    
319
    cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
320
    for(y = 0; y < s->total_height; y++) {
321
        c = &cells[y * s->width];
322
        if (w1 > 0) {
323
            c1 = &s->cells[y * last_width];
324
            for(x = 0; x < w1; x++) {
325
                *c++ = *c1++;
326
            }
327
        }
328
        for(x = w1; x < s->width; x++) {
329
            c->ch = ' ';
330
            c->fgcol = 7;
331
            c->bgcol = 0;
332
            c++;
333
        }
334
    }
335
    free(s->cells);
336
    s->cells = cells;
337
}
338

    
339
static void update_xy(TextConsole *s, int x, int y)
340
{
341
    TextCell *c;
342
    int y1, y2;
343

    
344
    if (s == active_console) {
345
        y1 = (s->y_base + y) % s->total_height;
346
        y2 = y1 - s->y_displayed;
347
        if (y2 < 0)
348
            y2 += s->total_height;
349
        if (y2 < s->height) {
350
            c = &s->cells[y1 * s->width + x];
351
            vga_putcharxy(s->ds, x, y2, c->ch, 
352
                          color_table[c->fgcol], color_table[c->bgcol]);
353
            dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT, 
354
                       FONT_WIDTH, FONT_HEIGHT);
355
        }
356
    }
357
}
358

    
359
static void console_show_cursor(TextConsole *s, int show)
360
{
361
    TextCell *c;
362
    int y, y1;
363

    
364
    if (s == active_console) {
365
        y1 = (s->y_base + s->y) % s->total_height;
366
        y = y1 - s->y_displayed;
367
        if (y < 0)
368
            y += s->total_height;
369
        if (y < s->height) {
370
            c = &s->cells[y1 * s->width + s->x];
371
            if (show) {
372
                vga_putcharxy(s->ds, s->x, y, c->ch, 
373
                              color_table[0], color_table[7]);
374
            } else {
375
                vga_putcharxy(s->ds, s->x, y, c->ch, 
376
                              color_table[c->fgcol], color_table[c->bgcol]);
377
            }
378
            dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT, 
379
                       FONT_WIDTH, FONT_HEIGHT);
380
        }
381
    }
382
}
383

    
384
static void console_refresh(TextConsole *s)
385
{
386
    TextCell *c;
387
    int x, y, y1;
388

    
389
    if (s != active_console) 
390
        return;
391

    
392
    vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
393
                  color_table[0]);
394
    y1 = s->y_displayed;
395
    for(y = 0; y < s->height; y++) {
396
        c = s->cells + y1 * s->width;
397
        for(x = 0; x < s->width; x++) {
398
            vga_putcharxy(s->ds, x, y, c->ch, 
399
                          color_table[c->fgcol], color_table[c->bgcol]);
400
            c++;
401
        }
402
        if (++y1 == s->total_height)
403
            y1 = 0;
404
    }
405
    dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
406
    console_show_cursor(s, 1);
407
}
408

    
409
static void console_scroll(int ydelta)
410
{
411
    TextConsole *s;
412
    int i, y1;
413
    
414
    s = active_console;
415
    if (!s || !s->text_console)
416
        return;
417

    
418
    if (ydelta > 0) {
419
        for(i = 0; i < ydelta; i++) {
420
            if (s->y_displayed == s->y_base)
421
                break;
422
            if (++s->y_displayed == s->total_height)
423
                s->y_displayed = 0;
424
        }
425
    } else {
426
        ydelta = -ydelta;
427
        i = s->backscroll_height;
428
        if (i > s->total_height - s->height)
429
            i = s->total_height - s->height;
430
        y1 = s->y_base - i;
431
        if (y1 < 0)
432
            y1 += s->total_height;
433
        for(i = 0; i < ydelta; i++) {
434
            if (s->y_displayed == y1)
435
                break;
436
            if (--s->y_displayed < 0)
437
                s->y_displayed = s->total_height - 1;
438
        }
439
    }
440
    console_refresh(s);
441
}
442

    
443
static void console_put_lf(TextConsole *s)
444
{
445
    TextCell *c;
446
    int x, y1;
447

    
448
    s->x = 0;
449
    s->y++;
450
    if (s->y >= s->height) {
451
        s->y = s->height - 1;
452
        
453
        if (s->y_displayed == s->y_base) {
454
            if (++s->y_displayed == s->total_height)
455
                s->y_displayed = 0;
456
        }
457
        if (++s->y_base == s->total_height)
458
            s->y_base = 0;
459
        if (s->backscroll_height < s->total_height)
460
            s->backscroll_height++;
461
        y1 = (s->y_base + s->height - 1) % s->total_height;
462
        c = &s->cells[y1 * s->width];
463
        for(x = 0; x < s->width; x++) {
464
            c->ch = ' ';
465
            c->fgcol = s->fgcol;
466
            c->bgcol = s->bgcol;
467
            c++;
468
        }
469
        if (s == active_console && s->y_displayed == s->y_base) {
470
            vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0, 
471
                       s->width * FONT_WIDTH, 
472
                       (s->height - 1) * FONT_HEIGHT);
473
            vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
474
                          s->width * FONT_WIDTH, FONT_HEIGHT, 
475
                          color_table[s->bgcol]);
476
            dpy_update(s->ds, 0, 0, 
477
                       s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
478
        }
479
    }
480
}
481

    
482
static void console_putchar(TextConsole *s, int ch)
483
{
484
    TextCell *c;
485
    int y1, i, x;
486

    
487
    switch(s->state) {
488
    case TTY_STATE_NORM:
489
        switch(ch) {
490
        case '\r':
491
            s->x = 0;
492
            break;
493
        case '\n':
494
            console_put_lf(s);
495
            break;
496
        case 27:
497
            s->state = TTY_STATE_ESC;
498
            break;
499
        default:
500
            y1 = (s->y_base + s->y) % s->total_height;
501
            c = &s->cells[y1 * s->width + s->x];
502
            c->ch = ch;
503
            c->fgcol = s->fgcol;
504
            c->bgcol = s->bgcol;
505
            update_xy(s, s->x, s->y);
506
            s->x++;
507
            if (s->x >= s->width)
508
                console_put_lf(s);
509
            break;
510
        }
511
        break;
512
    case TTY_STATE_ESC:
513
        if (ch == '[') {
514
            for(i=0;i<MAX_ESC_PARAMS;i++)
515
                s->esc_params[i] = 0;
516
            s->nb_esc_params = 0;
517
            s->state = TTY_STATE_CSI;
518
        } else {
519
            s->state = TTY_STATE_NORM;
520
        }
521
        break;
522
    case TTY_STATE_CSI:
523
        if (ch >= '0' && ch <= '9') {
524
            if (s->nb_esc_params < MAX_ESC_PARAMS) {
525
                s->esc_params[s->nb_esc_params] = 
526
                    s->esc_params[s->nb_esc_params] * 10 + ch - '0';
527
            }
528
        } else {
529
            s->nb_esc_params++;
530
            if (ch == ';')
531
                break;
532
            s->state = TTY_STATE_NORM;
533
            switch(ch) {
534
            case 'D':
535
                if (s->x > 0)
536
                    s->x--;
537
                break;
538
            case 'C':
539
                if (s->x < (s->width - 1))
540
                    s->x++;
541
                break;
542
            case 'K':
543
                /* clear to eol */
544
                y1 = (s->y_base + s->y) % s->total_height;
545
                for(x = s->x; x < s->width; x++) {
546
                    c = &s->cells[y1 * s->width + x];
547
                    c->ch = ' ';
548
                    c->fgcol = s->fgcol;
549
                    c->bgcol = s->bgcol;
550
                    c++;
551
                    update_xy(s, x, s->y);
552
                }
553
                break;
554
            default:
555
                break;
556
            }
557
            break;
558
        }
559
    }
560
}
561

    
562
void console_select(unsigned int index)
563
{
564
    TextConsole *s;
565
    
566
    if (index >= MAX_CONSOLES)
567
        return;
568
    s = consoles[index];
569
    if (s) {
570
        active_console = s;
571
        if (s->text_console) {
572
            if (s->g_width != s->ds->width ||
573
                s->g_height != s->ds->height) {
574
                s->g_width = s->ds->width;
575
                s->g_height = s->ds->height;
576
                text_console_resize(s);
577
            }
578
            console_refresh(s);
579
        }
580
    }
581
}
582

    
583
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
584
{
585
    TextConsole *s = chr->opaque;
586
    int i;
587

    
588
    console_show_cursor(s, 0);
589
    for(i = 0; i < len; i++) {
590
        console_putchar(s, buf[i]);
591
    }
592
    console_show_cursor(s, 1);
593
    return len;
594
}
595

    
596
static void console_chr_add_read_handler(CharDriverState *chr, 
597
                                         IOCanRWHandler *fd_can_read, 
598
                                         IOReadHandler *fd_read, void *opaque)
599
{
600
    TextConsole *s = chr->opaque;
601
    s->fd_read = fd_read;
602
    s->fd_opaque = opaque;
603
}
604

    
605
static void console_send_event(CharDriverState *chr, int event)
606
{
607
    TextConsole *s = chr->opaque;
608
    int i;
609

    
610
    if (event == CHR_EVENT_FOCUS) {
611
        for(i = 0; i < nb_consoles; i++) {
612
            if (consoles[i] == s) {
613
                console_select(i);
614
                break;
615
            }
616
        }
617
    }
618
}
619

    
620
/* called when an ascii key is pressed */
621
void kbd_put_keysym(int keysym)
622
{
623
    TextConsole *s;
624
    uint8_t buf[16], *q;
625
    int c;
626

    
627
    s = active_console;
628
    if (!s || !s->text_console)
629
        return;
630

    
631
    switch(keysym) {
632
    case QEMU_KEY_CTRL_UP:
633
        console_scroll(-1);
634
        break;
635
    case QEMU_KEY_CTRL_DOWN:
636
        console_scroll(1);
637
        break;
638
    case QEMU_KEY_CTRL_PAGEUP:
639
        console_scroll(-10);
640
        break;
641
    case QEMU_KEY_CTRL_PAGEDOWN:
642
        console_scroll(10);
643
        break;
644
    default:
645
        if (s->fd_read) {
646
            /* convert the QEMU keysym to VT100 key string */
647
            q = buf;
648
            if (keysym >= 0xe100 && keysym <= 0xe11f) {
649
                *q++ = '\033';
650
                *q++ = '[';
651
                c = keysym - 0xe100;
652
                if (c >= 10)
653
                    *q++ = '0' + (c / 10);
654
                *q++ = '0' + (c % 10);
655
                *q++ = '~';
656
            } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
657
                *q++ = '\033';
658
                *q++ = '[';
659
                *q++ = keysym & 0xff;
660
            } else {
661
                *q++ = keysym;
662
            }
663
            s->fd_read(s->fd_opaque, buf, q - buf);
664
        }
665
        break;
666
    }
667
}
668

    
669
TextConsole *graphic_console_init(DisplayState *ds)
670
{
671
    TextConsole *s;
672

    
673
    if (nb_consoles >= MAX_CONSOLES)
674
        return NULL;
675
    s = qemu_mallocz(sizeof(TextConsole));
676
    if (!s) {
677
        return NULL;
678
    }
679
    if (!active_console)
680
        active_console = s;
681
    s->ds = ds;
682
    consoles[nb_consoles++] = s;
683
    return s;
684
}
685

    
686
int is_active_console(TextConsole *s)
687
{
688
    return s == active_console;
689
}
690

    
691
CharDriverState *text_console_init(DisplayState *ds)
692
{
693
    CharDriverState *chr;
694
    TextConsole *s;
695
    int i;
696
    static int color_inited;
697
    
698
    chr = qemu_mallocz(sizeof(CharDriverState));
699
    if (!chr)
700
        return NULL;
701
    s = graphic_console_init(ds);
702
    if (!s) {
703
        free(chr);
704
        return NULL;
705
    }
706
    s->text_console = 1;
707
    chr->opaque = s;
708
    chr->chr_write = console_puts;
709
    chr->chr_add_read_handler = console_chr_add_read_handler;
710
    chr->chr_send_event = console_send_event;
711

    
712
    if (!color_inited) {
713
        color_inited = 1;
714
        for(i = 0; i < 8; i++) {
715
            color_table[i] = col_expand(s->ds, 
716
                                        vga_get_color(s->ds, color_table_rgb[i]));
717
        }
718
    }
719
    s->y_displayed = 0;
720
    s->y_base = 0;
721
    s->total_height = DEFAULT_BACKSCROLL;
722
    s->x = 0;
723
    s->y = 0;
724
    s->fgcol = 7;
725
    s->bgcol = 0;
726
    s->g_width = s->ds->width;
727
    s->g_height = s->ds->height;
728
    text_console_resize(s);
729

    
730
    return chr;
731
}