Statistics
| Branch: | Revision:

root / console.c @ 07ef34c3

History | View | Annotate | Download (38.2 kB)

1
/*
2
 * QEMU graphical console
3
 *
4
 * Copyright (c) 2004 Fabrice Bellard
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
#include "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
    TEXT_CONSOLE_FIXED_SIZE
114
} console_type_t;
115

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

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

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

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

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

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

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

    
169
void vga_hw_screen_dump(const char *filename)
170
{
171
    TextConsole *previous_active_console;
172

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

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

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

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

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

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

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

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

    
291
/***********************************************************/
292
/* basic char display */
293

    
294
#define FONT_HEIGHT 16
295
#define FONT_WIDTH 8
296

    
297
#include "vgafont.h"
298

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1161
    if (s->g_width != ds_get_width(s->ds) || s->g_height != ds_get_height(s->ds)) {
1162
        if (s->console_type == TEXT_CONSOLE_FIXED_SIZE)
1163
            dpy_resize(s->ds, s->g_width, s->g_height);
1164
        else {
1165
            s->g_width = ds_get_width(s->ds);
1166
            s->g_height = ds_get_height(s->ds);
1167
            text_console_resize(s);
1168
        }
1169
    }
1170
    console_refresh(s);
1171
}
1172

    
1173
static void text_console_update(void *opaque, console_ch_t *chardata)
1174
{
1175
    TextConsole *s = (TextConsole *) opaque;
1176
    int i, j, src;
1177

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

    
1200
static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1201
{
1202
    TextConsole *s;
1203
    int i;
1204

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

    
1231
TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
1232
                                  vga_hw_invalidate_ptr invalidate,
1233
                                  vga_hw_screen_dump_ptr screen_dump,
1234
                                  vga_hw_text_update_ptr text_update,
1235
                                  void *opaque)
1236
{
1237
    TextConsole *s;
1238

    
1239
    s = new_console(ds, GRAPHIC_CONSOLE);
1240
    if (!s)
1241
      return NULL;
1242
    s->hw_update = update;
1243
    s->hw_invalidate = invalidate;
1244
    s->hw_screen_dump = screen_dump;
1245
    s->hw_text_update = text_update;
1246
    s->hw = opaque;
1247
    return s;
1248
}
1249

    
1250
int is_graphic_console(void)
1251
{
1252
    return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1253
}
1254

    
1255
int is_fixedsize_console(void)
1256
{
1257
    return active_console && active_console->console_type != TEXT_CONSOLE;
1258
}
1259

    
1260
void console_color_init(DisplayState *ds)
1261
{
1262
    int i, j;
1263
    for (j = 0; j < 2; j++) {
1264
        for (i = 0; i < 8; i++) {
1265
            color_table[j][i] = col_expand(ds, 
1266
                   vga_get_color(ds, color_table_rgb[j][i]));
1267
        }
1268
    }
1269
}
1270

    
1271
CharDriverState *text_console_init(DisplayState *ds, const char *p)
1272
{
1273
    CharDriverState *chr;
1274
    TextConsole *s;
1275
    unsigned width;
1276
    unsigned height;
1277
    static int color_inited;
1278

    
1279
    chr = qemu_mallocz(sizeof(CharDriverState));
1280
    if (!chr)
1281
        return NULL;
1282
    s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
1283
    if (!s) {
1284
        free(chr);
1285
        return NULL;
1286
    }
1287
    chr->opaque = s;
1288
    chr->chr_write = console_puts;
1289
    chr->chr_send_event = console_send_event;
1290

    
1291
    s->chr = chr;
1292
    s->out_fifo.buf = s->out_fifo_buf;
1293
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1294
    s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
1295

    
1296
    if (!color_inited) {
1297
        color_inited = 1;
1298
        console_color_init(s->ds);
1299
    }
1300
    s->y_displayed = 0;
1301
    s->y_base = 0;
1302
    s->total_height = DEFAULT_BACKSCROLL;
1303
    s->x = 0;
1304
    s->y = 0;
1305
    width = ds_get_width(s->ds);
1306
    height = ds_get_height(s->ds);
1307
    if (p != 0) {
1308
        width = strtoul(p, (char **)&p, 10);
1309
        if (*p == 'C') {
1310
            p++;
1311
            width *= FONT_WIDTH;
1312
        }
1313
        if (*p == 'x') {
1314
            p++;
1315
            height = strtoul(p, (char **)&p, 10);
1316
            if (*p == 'C') {
1317
                p++;
1318
                height *= FONT_HEIGHT;
1319
            }
1320
        }
1321
    }
1322
    s->g_width = width;
1323
    s->g_height = height;
1324

    
1325
    s->hw_invalidate = text_console_invalidate;
1326
    s->hw_text_update = text_console_update;
1327
    s->hw = s;
1328

    
1329
    /* Set text attribute defaults */
1330
    s->t_attrib_default.bold = 0;
1331
    s->t_attrib_default.uline = 0;
1332
    s->t_attrib_default.blink = 0;
1333
    s->t_attrib_default.invers = 0;
1334
    s->t_attrib_default.unvisible = 0;
1335
    s->t_attrib_default.fgcol = COLOR_WHITE;
1336
    s->t_attrib_default.bgcol = COLOR_BLACK;
1337

    
1338
    /* set current text attributes to default */
1339
    s->t_attrib = s->t_attrib_default;
1340
    text_console_resize(s);
1341

    
1342
    qemu_chr_reset(chr);
1343

    
1344
    return chr;
1345
}
1346

    
1347
void qemu_console_resize(QEMUConsole *console, int width, int height)
1348
{
1349
    if (console->g_width != width || console->g_height != height
1350
        || !ds_get_data(console->ds)) {
1351
        console->g_width = width;
1352
        console->g_height = height;
1353
        if (active_console == console) {
1354
            dpy_resize(console->ds, width, height);
1355
        }
1356
    }
1357
}
1358

    
1359
void qemu_console_copy(QEMUConsole *console, int src_x, int src_y,
1360
                int dst_x, int dst_y, int w, int h)
1361
{
1362
    if (active_console == console) {
1363
        if (console->ds->dpy_copy)
1364
            console->ds->dpy_copy(console->ds,
1365
                            src_x, src_y, dst_x, dst_y, w, h);
1366
        else {
1367
            /* TODO */
1368
            console->ds->dpy_update(console->ds, dst_x, dst_y, w, h);
1369
        }
1370
    }
1371
}