Statistics
| Branch: | Revision:

root / ui / console.c @ dccfcd0e

History | View | Annotate | Download (50.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 "ui/console.h"
26
#include "qemu/timer.h"
27
#include "qmp-commands.h"
28
#include "sysemu/char.h"
29

    
30
//#define DEBUG_CONSOLE
31
#define DEFAULT_BACKSCROLL 512
32
#define MAX_CONSOLES 12
33
#define CONSOLE_CURSOR_PERIOD 500
34

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

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

    
48
typedef struct TextCell {
49
    uint8_t ch;
50
    TextAttributes t_attrib;
51
} TextCell;
52

    
53
#define MAX_ESC_PARAMS 3
54

    
55
enum TTYState {
56
    TTY_STATE_NORM,
57
    TTY_STATE_ESC,
58
    TTY_STATE_CSI,
59
};
60

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

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

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

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

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

    
112
typedef enum {
113
    GRAPHIC_CONSOLE,
114
    TEXT_CONSOLE,
115
    TEXT_CONSOLE_FIXED_SIZE
116
} console_type_t;
117

    
118
struct QemuConsole {
119
    int index;
120
    console_type_t console_type;
121
    DisplayState *ds;
122

    
123
    /* Graphic console state.  */
124
    vga_hw_update_ptr hw_update;
125
    vga_hw_invalidate_ptr hw_invalidate;
126
    vga_hw_screen_dump_ptr hw_screen_dump;
127
    vga_hw_text_update_ptr hw_text_update;
128
    void *hw;
129
    int g_width, g_height;
130

    
131
    /* Text console state */
132
    int width;
133
    int height;
134
    int total_height;
135
    int backscroll_height;
136
    int x, y;
137
    int x_saved, y_saved;
138
    int y_displayed;
139
    int y_base;
140
    TextAttributes t_attrib_default; /* default text attributes */
141
    TextAttributes t_attrib; /* currently active text attributes */
142
    TextCell *cells;
143
    int text_x[2], text_y[2], cursor_invalidate;
144
    int echo;
145
    bool cursor_visible_phase;
146
    QEMUTimer *cursor_timer;
147

    
148
    int update_x0;
149
    int update_y0;
150
    int update_x1;
151
    int update_y1;
152

    
153
    enum TTYState state;
154
    int esc_params[MAX_ESC_PARAMS];
155
    int nb_esc_params;
156

    
157
    CharDriverState *chr;
158
    /* fifo for key pressed */
159
    QEMUFIFO out_fifo;
160
    uint8_t out_fifo_buf[16];
161
    QEMUTimer *kbd_timer;
162
};
163

    
164
static DisplayState *display_state;
165
static QemuConsole *active_console;
166
static QemuConsole *consoles[MAX_CONSOLES];
167
static int nb_consoles = 0;
168

    
169
void vga_hw_update(void)
170
{
171
    if (active_console && active_console->hw_update)
172
        active_console->hw_update(active_console->hw);
173
}
174

    
175
void vga_hw_invalidate(void)
176
{
177
    if (active_console && active_console->hw_invalidate)
178
        active_console->hw_invalidate(active_console->hw);
179
}
180

    
181
void qmp_screendump(const char *filename, Error **errp)
182
{
183
    QemuConsole *previous_active_console;
184
    bool cswitch;
185

    
186
    previous_active_console = active_console;
187
    cswitch = previous_active_console && previous_active_console->index != 0;
188

    
189
    /* There is currently no way of specifying which screen we want to dump,
190
       so always dump the first one.  */
191
    if (cswitch) {
192
        console_select(0);
193
    }
194
    if (consoles[0] && consoles[0]->hw_screen_dump) {
195
        consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp);
196
    } else {
197
        error_setg(errp, "device doesn't support screendump");
198
    }
199

    
200
    if (cswitch) {
201
        console_select(previous_active_console->index);
202
    }
203
}
204

    
205
void vga_hw_text_update(console_ch_t *chardata)
206
{
207
    if (active_console && active_console->hw_text_update)
208
        active_console->hw_text_update(active_console->hw, chardata);
209
}
210

    
211
static void vga_fill_rect(QemuConsole *con,
212
                          int posx, int posy, int width, int height,
213
                          uint32_t color)
214
{
215
    DisplaySurface *surface = qemu_console_surface(con);
216
    uint8_t *d, *d1;
217
    int x, y, bpp;
218

    
219
    bpp = surface_bytes_per_pixel(surface);
220
    d1 = surface_data(surface) +
221
        surface_stride(surface) * posy + bpp * posx;
222
    for (y = 0; y < height; y++) {
223
        d = d1;
224
        switch(bpp) {
225
        case 1:
226
            for (x = 0; x < width; x++) {
227
                *((uint8_t *)d) = color;
228
                d++;
229
            }
230
            break;
231
        case 2:
232
            for (x = 0; x < width; x++) {
233
                *((uint16_t *)d) = color;
234
                d += 2;
235
            }
236
            break;
237
        case 4:
238
            for (x = 0; x < width; x++) {
239
                *((uint32_t *)d) = color;
240
                d += 4;
241
            }
242
            break;
243
        }
244
        d1 += surface_stride(surface);
245
    }
246
}
247

    
248
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
249
static void vga_bitblt(QemuConsole *con,
250
                       int xs, int ys, int xd, int yd, int w, int h)
251
{
252
    DisplaySurface *surface = qemu_console_surface(con);
253
    const uint8_t *s;
254
    uint8_t *d;
255
    int wb, y, bpp;
256

    
257
    bpp = surface_bytes_per_pixel(surface);
258
    wb = w * bpp;
259
    if (yd <= ys) {
260
        s = surface_data(surface) +
261
            surface_stride(surface) * ys + bpp * xs;
262
        d = surface_data(surface) +
263
            surface_stride(surface) * yd + bpp * xd;
264
        for (y = 0; y < h; y++) {
265
            memmove(d, s, wb);
266
            d += surface_stride(surface);
267
            s += surface_stride(surface);
268
        }
269
    } else {
270
        s = surface_data(surface) +
271
            surface_stride(surface) * (ys + h - 1) + bpp * xs;
272
        d = surface_data(surface) +
273
            surface_stride(surface) * (yd + h - 1) + bpp * xd;
274
       for (y = 0; y < h; y++) {
275
            memmove(d, s, wb);
276
            d -= surface_stride(surface);
277
            s -= surface_stride(surface);
278
        }
279
    }
280
}
281

    
282
/***********************************************************/
283
/* basic char display */
284

    
285
#define FONT_HEIGHT 16
286
#define FONT_WIDTH 8
287

    
288
#include "vgafont.h"
289

    
290
#define cbswap_32(__x) \
291
((uint32_t)( \
292
                (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
293
                (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
294
                (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
295
                (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
296

    
297
#ifdef HOST_WORDS_BIGENDIAN
298
#define PAT(x) x
299
#else
300
#define PAT(x) cbswap_32(x)
301
#endif
302

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

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

    
329
#ifndef CONFIG_CURSES
330
enum color_names {
331
    COLOR_BLACK   = 0,
332
    COLOR_RED     = 1,
333
    COLOR_GREEN   = 2,
334
    COLOR_YELLOW  = 3,
335
    COLOR_BLUE    = 4,
336
    COLOR_MAGENTA = 5,
337
    COLOR_CYAN    = 6,
338
    COLOR_WHITE   = 7
339
};
340
#endif
341

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

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

    
394
    printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
395
}
396
#endif
397

    
398
static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
399
                          TextAttributes *t_attrib)
400
{
401
    DisplaySurface *surface = qemu_console_surface(s);
402
    uint8_t *d;
403
    const uint8_t *font_ptr;
404
    unsigned int font_data, linesize, xorcol, bpp;
405
    int i;
406
    unsigned int fgcol, bgcol;
407

    
408
#ifdef DEBUG_CONSOLE
409
    printf("x: %2i y: %2i", x, y);
410
    console_print_text_attributes(t_attrib, ch);
411
#endif
412

    
413
    if (t_attrib->invers) {
414
        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
415
        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
416
    } else {
417
        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
418
        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
419
    }
420

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

    
475
static void text_console_resize(QemuConsole *s)
476
{
477
    TextCell *cells, *c, *c1;
478
    int w1, x, y, last_width;
479

    
480
    last_width = s->width;
481
    s->width = s->g_width / FONT_WIDTH;
482
    s->height = s->g_height / FONT_HEIGHT;
483

    
484
    w1 = last_width;
485
    if (s->width < w1)
486
        w1 = s->width;
487

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

    
507
static inline void text_update_xy(QemuConsole *s, int x, int y)
508
{
509
    s->text_x[0] = MIN(s->text_x[0], x);
510
    s->text_x[1] = MAX(s->text_x[1], x);
511
    s->text_y[0] = MIN(s->text_y[0], y);
512
    s->text_y[1] = MAX(s->text_y[1], y);
513
}
514

    
515
static void invalidate_xy(QemuConsole *s, int x, int y)
516
{
517
    if (s->update_x0 > x * FONT_WIDTH)
518
        s->update_x0 = x * FONT_WIDTH;
519
    if (s->update_y0 > y * FONT_HEIGHT)
520
        s->update_y0 = y * FONT_HEIGHT;
521
    if (s->update_x1 < (x + 1) * FONT_WIDTH)
522
        s->update_x1 = (x + 1) * FONT_WIDTH;
523
    if (s->update_y1 < (y + 1) * FONT_HEIGHT)
524
        s->update_y1 = (y + 1) * FONT_HEIGHT;
525
}
526

    
527
static void update_xy(QemuConsole *s, int x, int y)
528
{
529
    TextCell *c;
530
    int y1, y2;
531

    
532
    if (s != active_console) {
533
        return;
534
    }
535

    
536
    if (s->ds->have_text) {
537
        text_update_xy(s, x, y);
538
    }
539

    
540
    if (s->ds->have_gfx) {
541
        y1 = (s->y_base + y) % s->total_height;
542
        y2 = y1 - s->y_displayed;
543
        if (y2 < 0)
544
            y2 += s->total_height;
545
        if (y2 < s->height) {
546
            c = &s->cells[y1 * s->width + x];
547
            vga_putcharxy(s, x, y2, c->ch,
548
                          &(c->t_attrib));
549
            invalidate_xy(s, x, y2);
550
        }
551
    }
552
}
553

    
554
static void console_show_cursor(QemuConsole *s, int show)
555
{
556
    TextCell *c;
557
    int y, y1;
558
    int x = s->x;
559

    
560
    if (s != active_console) {
561
        return;
562
    }
563

    
564
    if (s->ds->have_text) {
565
        s->cursor_invalidate = 1;
566
    }
567

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

    
590
static void console_refresh(QemuConsole *s)
591
{
592
    DisplaySurface *surface = qemu_console_surface(s);
593
    TextCell *c;
594
    int x, y, y1;
595

    
596
    if (s != active_console)
597
        return;
598

    
599
    if (s->ds->have_text) {
600
        s->text_x[0] = 0;
601
        s->text_y[0] = 0;
602
        s->text_x[1] = s->width - 1;
603
        s->text_y[1] = s->height - 1;
604
        s->cursor_invalidate = 1;
605
    }
606

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

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

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

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

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

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

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

    
694
            if (s->ds->have_gfx) {
695
                vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
696
                           s->width * FONT_WIDTH,
697
                           (s->height - 1) * FONT_HEIGHT);
698
                vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
699
                              s->width * FONT_WIDTH, FONT_HEIGHT,
700
                              color_table_rgb[0][s->t_attrib_default.bgcol]);
701
                s->update_x0 = 0;
702
                s->update_y0 = 0;
703
                s->update_x1 = s->width * FONT_WIDTH;
704
                s->update_y1 = s->height * FONT_HEIGHT;
705
            }
706
        }
707
    }
708
}
709

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

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

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

    
816
/* set cursor, checking bounds */
817
static void set_cursor(QemuConsole *s, int x, int y)
818
{
819
    if (x < 0) {
820
        x = 0;
821
    }
822
    if (y < 0) {
823
        y = 0;
824
    }
825
    if (y >= s->height) {
826
        y = s->height - 1;
827
    }
828
    if (x >= s->width) {
829
        x = s->width - 1;
830
    }
831

    
832
    s->x = x;
833
    s->y = y;
834
}
835

    
836
static void console_putchar(QemuConsole *s, int ch)
837
{
838
    TextCell *c;
839
    int y1, i;
840
    int x, y;
841

    
842
    switch(s->state) {
843
    case TTY_STATE_NORM:
844
        switch(ch) {
845
        case '\r':  /* carriage return */
846
            s->x = 0;
847
            break;
848
        case '\n':  /* newline */
849
            console_put_lf(s);
850
            break;
851
        case '\b':  /* backspace */
852
            if (s->x > 0)
853
                s->x--;
854
            break;
855
        case '\t':  /* tabspace */
856
            if (s->x + (8 - (s->x % 8)) > s->width) {
857
                s->x = 0;
858
                console_put_lf(s);
859
            } else {
860
                s->x = s->x + (8 - (s->x % 8));
861
            }
862
            break;
863
        case '\a':  /* alert aka. bell */
864
            /* TODO: has to be implemented */
865
            break;
866
        case 14:
867
            /* SI (shift in), character set 0 (ignored) */
868
            break;
869
        case 15:
870
            /* SO (shift out), character set 1 (ignored) */
871
            break;
872
        case 27:    /* esc (introducing an escape sequence) */
873
            s->state = TTY_STATE_ESC;
874
            break;
875
        default:
876
            if (s->x >= s->width) {
877
                /* line wrap */
878
                s->x = 0;
879
                console_put_lf(s);
880
            }
881
            y1 = (s->y_base + s->y) % s->total_height;
882
            c = &s->cells[y1 * s->width + s->x];
883
            c->ch = ch;
884
            c->t_attrib = s->t_attrib;
885
            update_xy(s, s->x, s->y);
886
            s->x++;
887
            break;
888
        }
889
        break;
890
    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
891
        if (ch == '[') {
892
            for(i=0;i<MAX_ESC_PARAMS;i++)
893
                s->esc_params[i] = 0;
894
            s->nb_esc_params = 0;
895
            s->state = TTY_STATE_CSI;
896
        } else {
897
            s->state = TTY_STATE_NORM;
898
        }
899
        break;
900
    case TTY_STATE_CSI: /* handle escape sequence parameters */
901
        if (ch >= '0' && ch <= '9') {
902
            if (s->nb_esc_params < MAX_ESC_PARAMS) {
903
                int *param = &s->esc_params[s->nb_esc_params];
904
                int digit = (ch - '0');
905

    
906
                *param = (*param <= (INT_MAX - digit) / 10) ?
907
                         *param * 10 + digit : INT_MAX;
908
            }
909
        } else {
910
            if (s->nb_esc_params < MAX_ESC_PARAMS)
911
                s->nb_esc_params++;
912
            if (ch == ';')
913
                break;
914
#ifdef DEBUG_CONSOLE
915
            fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
916
                    s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
917
#endif
918
            s->state = TTY_STATE_NORM;
919
            switch(ch) {
920
            case 'A':
921
                /* move cursor up */
922
                if (s->esc_params[0] == 0) {
923
                    s->esc_params[0] = 1;
924
                }
925
                set_cursor(s, s->x, s->y - s->esc_params[0]);
926
                break;
927
            case 'B':
928
                /* move cursor down */
929
                if (s->esc_params[0] == 0) {
930
                    s->esc_params[0] = 1;
931
                }
932
                set_cursor(s, s->x, s->y + s->esc_params[0]);
933
                break;
934
            case 'C':
935
                /* move cursor right */
936
                if (s->esc_params[0] == 0) {
937
                    s->esc_params[0] = 1;
938
                }
939
                set_cursor(s, s->x + s->esc_params[0], s->y);
940
                break;
941
            case 'D':
942
                /* move cursor left */
943
                if (s->esc_params[0] == 0) {
944
                    s->esc_params[0] = 1;
945
                }
946
                set_cursor(s, s->x - s->esc_params[0], s->y);
947
                break;
948
            case 'G':
949
                /* move cursor to column */
950
                set_cursor(s, s->esc_params[0] - 1, s->y);
951
                break;
952
            case 'f':
953
            case 'H':
954
                /* move cursor to row, column */
955
                set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
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
                break;
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
    DisplaySurface *surface;
1044
    QemuConsole *s;
1045

    
1046
    if (index >= MAX_CONSOLES)
1047
        return;
1048
    if (active_console) {
1049
        surface = qemu_console_surface(active_console);
1050
        active_console->g_width = surface_width(surface);
1051
        active_console->g_height = surface_height(surface);
1052
    }
1053
    s = consoles[index];
1054
    if (s) {
1055
        DisplayState *ds = s->ds;
1056

    
1057
        if (active_console && active_console->cursor_timer) {
1058
            qemu_del_timer(active_console->cursor_timer);
1059
        }
1060
        active_console = s;
1061
        if (ds->have_gfx) {
1062
            surface = qemu_create_displaysurface(s->g_width, s->g_height);
1063
            dpy_gfx_replace_surface(s, surface);
1064
        }
1065
        if (ds->have_text) {
1066
            dpy_text_resize(s, s->width, s->height);
1067
        }
1068
        if (s->cursor_timer) {
1069
            qemu_mod_timer(s->cursor_timer,
1070
                   qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1071
        }
1072
        vga_hw_invalidate();
1073
    }
1074
}
1075

    
1076
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1077
{
1078
    QemuConsole *s = chr->opaque;
1079
    int i;
1080

    
1081
    s->update_x0 = s->width * FONT_WIDTH;
1082
    s->update_y0 = s->height * FONT_HEIGHT;
1083
    s->update_x1 = 0;
1084
    s->update_y1 = 0;
1085
    console_show_cursor(s, 0);
1086
    for(i = 0; i < len; i++) {
1087
        console_putchar(s, buf[i]);
1088
    }
1089
    console_show_cursor(s, 1);
1090
    if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
1091
        dpy_gfx_update(s, s->update_x0, s->update_y0,
1092
                       s->update_x1 - s->update_x0,
1093
                       s->update_y1 - s->update_y0);
1094
    }
1095
    return len;
1096
}
1097

    
1098
static void kbd_send_chars(void *opaque)
1099
{
1100
    QemuConsole *s = opaque;
1101
    int len;
1102
    uint8_t buf[16];
1103

    
1104
    len = qemu_chr_be_can_write(s->chr);
1105
    if (len > s->out_fifo.count)
1106
        len = s->out_fifo.count;
1107
    if (len > 0) {
1108
        if (len > sizeof(buf))
1109
            len = sizeof(buf);
1110
        qemu_fifo_read(&s->out_fifo, buf, len);
1111
        qemu_chr_be_write(s->chr, buf, len);
1112
    }
1113
    /* characters are pending: we send them a bit later (XXX:
1114
       horrible, should change char device API) */
1115
    if (s->out_fifo.count > 0) {
1116
        qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
1117
    }
1118
}
1119

    
1120
/* called when an ascii key is pressed */
1121
void kbd_put_keysym(int keysym)
1122
{
1123
    QemuConsole *s;
1124
    uint8_t buf[16], *q;
1125
    int c;
1126

    
1127
    s = active_console;
1128
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
1129
        return;
1130

    
1131
    switch(keysym) {
1132
    case QEMU_KEY_CTRL_UP:
1133
        console_scroll(-1);
1134
        break;
1135
    case QEMU_KEY_CTRL_DOWN:
1136
        console_scroll(1);
1137
        break;
1138
    case QEMU_KEY_CTRL_PAGEUP:
1139
        console_scroll(-10);
1140
        break;
1141
    case QEMU_KEY_CTRL_PAGEDOWN:
1142
        console_scroll(10);
1143
        break;
1144
    default:
1145
        /* convert the QEMU keysym to VT100 key string */
1146
        q = buf;
1147
        if (keysym >= 0xe100 && keysym <= 0xe11f) {
1148
            *q++ = '\033';
1149
            *q++ = '[';
1150
            c = keysym - 0xe100;
1151
            if (c >= 10)
1152
                *q++ = '0' + (c / 10);
1153
            *q++ = '0' + (c % 10);
1154
            *q++ = '~';
1155
        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1156
            *q++ = '\033';
1157
            *q++ = '[';
1158
            *q++ = keysym & 0xff;
1159
        } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1160
            console_puts(s->chr, (const uint8_t *) "\r", 1);
1161
            *q++ = '\n';
1162
        } else {
1163
            *q++ = keysym;
1164
        }
1165
        if (s->echo) {
1166
            console_puts(s->chr, buf, q - buf);
1167
        }
1168
        if (s->chr->chr_read) {
1169
            qemu_fifo_write(&s->out_fifo, buf, q - buf);
1170
            kbd_send_chars(s);
1171
        }
1172
        break;
1173
    }
1174
}
1175

    
1176
static void text_console_invalidate(void *opaque)
1177
{
1178
    QemuConsole *s = (QemuConsole *) opaque;
1179
    DisplaySurface *surface = qemu_console_surface(s);
1180

    
1181
    if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1182
        s->g_width = surface_width(surface);
1183
        s->g_height = surface_height(surface);
1184
        text_console_resize(s);
1185
    }
1186
    console_refresh(s);
1187
}
1188

    
1189
static void text_console_update(void *opaque, console_ch_t *chardata)
1190
{
1191
    QemuConsole *s = (QemuConsole *) opaque;
1192
    int i, j, src;
1193

    
1194
    if (s->text_x[0] <= s->text_x[1]) {
1195
        src = (s->y_base + s->text_y[0]) * s->width;
1196
        chardata += s->text_y[0] * s->width;
1197
        for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1198
            for (j = 0; j < s->width; j ++, src ++)
1199
                console_write_ch(chardata ++, s->cells[src].ch |
1200
                                (s->cells[src].t_attrib.fgcol << 12) |
1201
                                (s->cells[src].t_attrib.bgcol << 8) |
1202
                                (s->cells[src].t_attrib.bold << 21));
1203
        dpy_text_update(s, s->text_x[0], s->text_y[0],
1204
                        s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1205
        s->text_x[0] = s->width;
1206
        s->text_y[0] = s->height;
1207
        s->text_x[1] = 0;
1208
        s->text_y[1] = 0;
1209
    }
1210
    if (s->cursor_invalidate) {
1211
        dpy_text_cursor(s, s->x, s->y);
1212
        s->cursor_invalidate = 0;
1213
    }
1214
}
1215

    
1216
static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
1217
{
1218
    QemuConsole *s;
1219
    int i;
1220

    
1221
    if (nb_consoles >= MAX_CONSOLES)
1222
        return NULL;
1223
    s = g_malloc0(sizeof(QemuConsole));
1224
    if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1225
        (console_type == GRAPHIC_CONSOLE))) {
1226
        active_console = s;
1227
    }
1228
    s->ds = ds;
1229
    s->console_type = console_type;
1230
    if (console_type != GRAPHIC_CONSOLE) {
1231
        s->index = nb_consoles;
1232
        consoles[nb_consoles++] = s;
1233
    } else {
1234
        /* HACK: Put graphical consoles before text consoles.  */
1235
        for (i = nb_consoles; i > 0; i--) {
1236
            if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1237
                break;
1238
            consoles[i] = consoles[i - 1];
1239
            consoles[i]->index = i;
1240
        }
1241
        s->index = i;
1242
        consoles[i] = s;
1243
        nb_consoles++;
1244
    }
1245
    return s;
1246
}
1247

    
1248
static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1249
                               int linesize, PixelFormat pf, int newflags)
1250
{
1251
    surface->pf = pf;
1252

    
1253
    qemu_pixman_image_unref(surface->image);
1254
    surface->image = NULL;
1255

    
1256
    surface->format = qemu_pixman_get_format(&pf);
1257
    assert(surface->format != 0);
1258
    surface->image = pixman_image_create_bits(surface->format,
1259
                                              width, height,
1260
                                              NULL, linesize);
1261
    assert(surface->image != NULL);
1262

    
1263
    surface->flags = newflags | QEMU_ALLOCATED_FLAG;
1264
#ifdef HOST_WORDS_BIGENDIAN
1265
    surface->flags |= QEMU_BIG_ENDIAN_FLAG;
1266
#endif
1267
}
1268

    
1269
DisplaySurface *qemu_create_displaysurface(int width, int height)
1270
{
1271
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1272
    int linesize = width * 4;
1273

    
1274
    trace_displaysurface_create(surface, width, height);
1275
    qemu_alloc_display(surface, width, height, linesize,
1276
                       qemu_default_pixelformat(32), 0);
1277
    return surface;
1278
}
1279

    
1280
DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
1281
                                                int linesize, uint8_t *data,
1282
                                                bool byteswap)
1283
{
1284
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1285

    
1286
    trace_displaysurface_create_from(surface, width, height, bpp, byteswap);
1287
    if (byteswap) {
1288
        surface->pf = qemu_different_endianness_pixelformat(bpp);
1289
    } else {
1290
        surface->pf = qemu_default_pixelformat(bpp);
1291
    }
1292

    
1293
    surface->format = qemu_pixman_get_format(&surface->pf);
1294
    assert(surface->format != 0);
1295
    surface->image = pixman_image_create_bits(surface->format,
1296
                                              width, height,
1297
                                              (void *)data, linesize);
1298
    assert(surface->image != NULL);
1299

    
1300
#ifdef HOST_WORDS_BIGENDIAN
1301
    surface->flags = QEMU_BIG_ENDIAN_FLAG;
1302
#endif
1303

    
1304
    return surface;
1305
}
1306

    
1307
void qemu_free_displaysurface(DisplaySurface *surface)
1308
{
1309
    if (surface == NULL) {
1310
        return;
1311
    }
1312
    trace_displaysurface_free(surface);
1313
    qemu_pixman_image_unref(surface->image);
1314
    g_free(surface);
1315
}
1316

    
1317
void register_displaychangelistener(DisplayState *ds,
1318
                                    DisplayChangeListener *dcl)
1319
{
1320
    trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1321
    dcl->ds = ds;
1322
    QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
1323
    gui_setup_refresh(ds);
1324
    if (dcl->ops->dpy_gfx_switch) {
1325
        dcl->ops->dpy_gfx_switch(dcl, ds->surface);
1326
    }
1327
}
1328

    
1329
void unregister_displaychangelistener(DisplayChangeListener *dcl)
1330
{
1331
    DisplayState *ds = dcl->ds;
1332
    trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1333
    QLIST_REMOVE(dcl, next);
1334
    gui_setup_refresh(ds);
1335
}
1336

    
1337
void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1338
{
1339
    DisplayState *s = con->ds;
1340
    struct DisplayChangeListener *dcl;
1341
    int width = pixman_image_get_width(s->surface->image);
1342
    int height = pixman_image_get_height(s->surface->image);
1343

    
1344
    x = MAX(x, 0);
1345
    y = MAX(y, 0);
1346
    x = MIN(x, width);
1347
    y = MIN(y, height);
1348
    w = MIN(w, width - x);
1349
    h = MIN(h, height - y);
1350

    
1351
    QLIST_FOREACH(dcl, &s->listeners, next) {
1352
        if (dcl->ops->dpy_gfx_update) {
1353
            dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1354
        }
1355
    }
1356
}
1357

    
1358
void dpy_gfx_replace_surface(QemuConsole *con,
1359
                             DisplaySurface *surface)
1360
{
1361
    DisplayState *s = con->ds;
1362
    DisplaySurface *old_surface = s->surface;
1363
    struct DisplayChangeListener *dcl;
1364

    
1365
    s->surface = surface;
1366
    QLIST_FOREACH(dcl, &s->listeners, next) {
1367
        if (dcl->ops->dpy_gfx_switch) {
1368
            dcl->ops->dpy_gfx_switch(dcl, surface);
1369
        }
1370
    }
1371
    qemu_free_displaysurface(old_surface);
1372
}
1373

    
1374
void dpy_refresh(DisplayState *s)
1375
{
1376
    struct DisplayChangeListener *dcl;
1377
    QLIST_FOREACH(dcl, &s->listeners, next) {
1378
        if (dcl->ops->dpy_refresh) {
1379
            dcl->ops->dpy_refresh(dcl);
1380
        }
1381
    }
1382
}
1383

    
1384
void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
1385
                  int dst_x, int dst_y, int w, int h)
1386
{
1387
    DisplayState *s = con->ds;
1388
    struct DisplayChangeListener *dcl;
1389
    QLIST_FOREACH(dcl, &s->listeners, next) {
1390
        if (dcl->ops->dpy_gfx_copy) {
1391
            dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
1392
        } else { /* TODO */
1393
            dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
1394
        }
1395
    }
1396
}
1397

    
1398
void dpy_text_cursor(QemuConsole *con, int x, int y)
1399
{
1400
    DisplayState *s = con->ds;
1401
    struct DisplayChangeListener *dcl;
1402
    QLIST_FOREACH(dcl, &s->listeners, next) {
1403
        if (dcl->ops->dpy_text_cursor) {
1404
            dcl->ops->dpy_text_cursor(dcl, x, y);
1405
        }
1406
    }
1407
}
1408

    
1409
void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1410
{
1411
    DisplayState *s = con->ds;
1412
    struct DisplayChangeListener *dcl;
1413
    QLIST_FOREACH(dcl, &s->listeners, next) {
1414
        if (dcl->ops->dpy_text_update) {
1415
            dcl->ops->dpy_text_update(dcl, x, y, w, h);
1416
        }
1417
    }
1418
}
1419

    
1420
void dpy_text_resize(QemuConsole *con, int w, int h)
1421
{
1422
    DisplayState *s = con->ds;
1423
    struct DisplayChangeListener *dcl;
1424
    QLIST_FOREACH(dcl, &s->listeners, next) {
1425
        if (dcl->ops->dpy_text_resize) {
1426
            dcl->ops->dpy_text_resize(dcl, w, h);
1427
        }
1428
    }
1429
}
1430

    
1431
void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1432
{
1433
    DisplayState *s = con->ds;
1434
    struct DisplayChangeListener *dcl;
1435
    QLIST_FOREACH(dcl, &s->listeners, next) {
1436
        if (dcl->ops->dpy_mouse_set) {
1437
            dcl->ops->dpy_mouse_set(dcl, x, y, on);
1438
        }
1439
    }
1440
}
1441

    
1442
void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1443
{
1444
    DisplayState *s = con->ds;
1445
    struct DisplayChangeListener *dcl;
1446
    QLIST_FOREACH(dcl, &s->listeners, next) {
1447
        if (dcl->ops->dpy_cursor_define) {
1448
            dcl->ops->dpy_cursor_define(dcl, cursor);
1449
        }
1450
    }
1451
}
1452

    
1453
bool dpy_cursor_define_supported(QemuConsole *con)
1454
{
1455
    DisplayState *s = con->ds;
1456
    struct DisplayChangeListener *dcl;
1457
    QLIST_FOREACH(dcl, &s->listeners, next) {
1458
        if (dcl->ops->dpy_cursor_define) {
1459
            return true;
1460
        }
1461
    }
1462
    return false;
1463
}
1464

    
1465
static void dumb_display_init(void)
1466
{
1467
    DisplayState *ds = g_malloc0(sizeof(DisplayState));
1468
    int width = 640;
1469
    int height = 480;
1470

    
1471
    if (is_fixedsize_console()) {
1472
        width = active_console->g_width;
1473
        height = active_console->g_height;
1474
    }
1475
    ds->surface = qemu_create_displaysurface(width, height);
1476

    
1477
    register_displaystate(ds);
1478
}
1479

    
1480
/***********************************************************/
1481
/* register display */
1482

    
1483
void register_displaystate(DisplayState *ds)
1484
{
1485
    DisplayState **s;
1486
    s = &display_state;
1487
    while (*s != NULL)
1488
        s = &(*s)->next;
1489
    ds->next = NULL;
1490
    *s = ds;
1491
}
1492

    
1493
DisplayState *get_displaystate(void)
1494
{
1495
    if (!display_state) {
1496
        dumb_display_init ();
1497
    }
1498
    return display_state;
1499
}
1500

    
1501
QemuConsole *graphic_console_init(vga_hw_update_ptr update,
1502
                                  vga_hw_invalidate_ptr invalidate,
1503
                                  vga_hw_screen_dump_ptr screen_dump,
1504
                                  vga_hw_text_update_ptr text_update,
1505
                                  void *opaque)
1506
{
1507
    QemuConsole *s;
1508
    DisplayState *ds;
1509

    
1510
    ds = (DisplayState *) g_malloc0(sizeof(DisplayState));
1511
    s = new_console(ds, GRAPHIC_CONSOLE);
1512
    s->hw_update = update;
1513
    s->hw_invalidate = invalidate;
1514
    s->hw_screen_dump = screen_dump;
1515
    s->hw_text_update = text_update;
1516
    s->hw = opaque;
1517

    
1518
    ds->surface = qemu_create_displaysurface(640, 480);
1519

    
1520
    register_displaystate(ds);
1521
    return s;
1522
}
1523

    
1524
int is_graphic_console(void)
1525
{
1526
    return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1527
}
1528

    
1529
int is_fixedsize_console(void)
1530
{
1531
    return active_console && active_console->console_type != TEXT_CONSOLE;
1532
}
1533

    
1534
static void text_console_set_echo(CharDriverState *chr, bool echo)
1535
{
1536
    QemuConsole *s = chr->opaque;
1537

    
1538
    s->echo = echo;
1539
}
1540

    
1541
static void text_console_update_cursor(void *opaque)
1542
{
1543
    QemuConsole *s = opaque;
1544

    
1545
    s->cursor_visible_phase = !s->cursor_visible_phase;
1546
    vga_hw_invalidate();
1547
    qemu_mod_timer(s->cursor_timer,
1548
                   qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1549
}
1550

    
1551
static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
1552
{
1553
    QemuConsole *s;
1554

    
1555
    s = chr->opaque;
1556

    
1557
    chr->chr_write = console_puts;
1558

    
1559
    s->out_fifo.buf = s->out_fifo_buf;
1560
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1561
    s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
1562
    s->ds = ds;
1563

    
1564
    s->y_displayed = 0;
1565
    s->y_base = 0;
1566
    s->total_height = DEFAULT_BACKSCROLL;
1567
    s->x = 0;
1568
    s->y = 0;
1569
    if (s->console_type == TEXT_CONSOLE) {
1570
        s->g_width = surface_width(s->ds->surface);
1571
        s->g_height = surface_height(s->ds->surface);
1572
    }
1573

    
1574
    s->cursor_timer =
1575
        qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
1576

    
1577
    s->hw_invalidate = text_console_invalidate;
1578
    s->hw_text_update = text_console_update;
1579
    s->hw = s;
1580

    
1581
    /* Set text attribute defaults */
1582
    s->t_attrib_default.bold = 0;
1583
    s->t_attrib_default.uline = 0;
1584
    s->t_attrib_default.blink = 0;
1585
    s->t_attrib_default.invers = 0;
1586
    s->t_attrib_default.unvisible = 0;
1587
    s->t_attrib_default.fgcol = COLOR_WHITE;
1588
    s->t_attrib_default.bgcol = COLOR_BLACK;
1589
    /* set current text attributes to default */
1590
    s->t_attrib = s->t_attrib_default;
1591
    text_console_resize(s);
1592

    
1593
    if (chr->label) {
1594
        char msg[128];
1595
        int len;
1596

    
1597
        s->t_attrib.bgcol = COLOR_BLUE;
1598
        len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1599
        console_puts(chr, (uint8_t*)msg, len);
1600
        s->t_attrib = s->t_attrib_default;
1601
    }
1602

    
1603
    qemu_chr_be_generic_open(chr);
1604
    if (chr->init)
1605
        chr->init(chr);
1606
}
1607

    
1608
static CharDriverState *text_console_init(ChardevVC *vc)
1609
{
1610
    CharDriverState *chr;
1611
    QemuConsole *s;
1612
    unsigned width = 0;
1613
    unsigned height = 0;
1614

    
1615
    chr = g_malloc0(sizeof(CharDriverState));
1616

    
1617
    if (vc->has_width) {
1618
        width = vc->width;
1619
    } else if (vc->has_cols) {
1620
        width = vc->cols * FONT_WIDTH;
1621
    }
1622

    
1623
    if (vc->has_height) {
1624
        height = vc->height;
1625
    } else if (vc->has_rows) {
1626
        height = vc->rows * FONT_HEIGHT;
1627
    }
1628

    
1629
    if (width == 0 || height == 0) {
1630
        s = new_console(NULL, TEXT_CONSOLE);
1631
    } else {
1632
        s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1633
    }
1634

    
1635
    if (!s) {
1636
        g_free(chr);
1637
        return NULL;
1638
    }
1639

    
1640
    s->chr = chr;
1641
    s->g_width = width;
1642
    s->g_height = height;
1643
    chr->opaque = s;
1644
    chr->chr_set_echo = text_console_set_echo;
1645
    return chr;
1646
}
1647

    
1648
static VcHandler *vc_handler = text_console_init;
1649

    
1650
CharDriverState *vc_init(ChardevVC *vc)
1651
{
1652
    return vc_handler(vc);
1653
}
1654

    
1655
void register_vc_handler(VcHandler *handler)
1656
{
1657
    vc_handler = handler;
1658
}
1659

    
1660
void text_consoles_set_display(DisplayState *ds)
1661
{
1662
    int i;
1663

    
1664
    for (i = 0; i < nb_consoles; i++) {
1665
        if (consoles[i]->console_type != GRAPHIC_CONSOLE) {
1666
            text_console_do_init(consoles[i]->chr, ds);
1667
        }
1668
    }
1669
}
1670

    
1671
void qemu_console_resize(QemuConsole *s, int width, int height)
1672
{
1673
    s->g_width = width;
1674
    s->g_height = height;
1675
    if (is_graphic_console()) {
1676
        DisplaySurface *surface;
1677
        surface = qemu_create_displaysurface(width, height);
1678
        dpy_gfx_replace_surface(s, surface);
1679
    }
1680
}
1681

    
1682
void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
1683
                       int dst_x, int dst_y, int w, int h)
1684
{
1685
    if (is_graphic_console()) {
1686
        dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
1687
    }
1688
}
1689

    
1690
DisplaySurface *qemu_console_surface(QemuConsole *console)
1691
{
1692
    return console->ds->surface;
1693
}
1694

    
1695
DisplayState *qemu_console_displaystate(QemuConsole *console)
1696
{
1697
    return console->ds;
1698
}
1699

    
1700
PixelFormat qemu_different_endianness_pixelformat(int bpp)
1701
{
1702
    PixelFormat pf;
1703

    
1704
    memset(&pf, 0x00, sizeof(PixelFormat));
1705

    
1706
    pf.bits_per_pixel = bpp;
1707
    pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1708
    pf.depth = bpp == 32 ? 24 : bpp;
1709

    
1710
    switch (bpp) {
1711
        case 24:
1712
            pf.rmask = 0x000000FF;
1713
            pf.gmask = 0x0000FF00;
1714
            pf.bmask = 0x00FF0000;
1715
            pf.rmax = 255;
1716
            pf.gmax = 255;
1717
            pf.bmax = 255;
1718
            pf.rshift = 0;
1719
            pf.gshift = 8;
1720
            pf.bshift = 16;
1721
            pf.rbits = 8;
1722
            pf.gbits = 8;
1723
            pf.bbits = 8;
1724
            break;
1725
        case 32:
1726
            pf.rmask = 0x0000FF00;
1727
            pf.gmask = 0x00FF0000;
1728
            pf.bmask = 0xFF000000;
1729
            pf.amask = 0x00000000;
1730
            pf.amax = 255;
1731
            pf.rmax = 255;
1732
            pf.gmax = 255;
1733
            pf.bmax = 255;
1734
            pf.ashift = 0;
1735
            pf.rshift = 8;
1736
            pf.gshift = 16;
1737
            pf.bshift = 24;
1738
            pf.rbits = 8;
1739
            pf.gbits = 8;
1740
            pf.bbits = 8;
1741
            pf.abits = 8;
1742
            break;
1743
        default:
1744
            break;
1745
    }
1746
    return pf;
1747
}
1748

    
1749
PixelFormat qemu_default_pixelformat(int bpp)
1750
{
1751
    PixelFormat pf;
1752

    
1753
    memset(&pf, 0x00, sizeof(PixelFormat));
1754

    
1755
    pf.bits_per_pixel = bpp;
1756
    pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1757
    pf.depth = bpp == 32 ? 24 : bpp;
1758

    
1759
    switch (bpp) {
1760
        case 15:
1761
            pf.bits_per_pixel = 16;
1762
            pf.rmask = 0x00007c00;
1763
            pf.gmask = 0x000003E0;
1764
            pf.bmask = 0x0000001F;
1765
            pf.rmax = 31;
1766
            pf.gmax = 31;
1767
            pf.bmax = 31;
1768
            pf.rshift = 10;
1769
            pf.gshift = 5;
1770
            pf.bshift = 0;
1771
            pf.rbits = 5;
1772
            pf.gbits = 5;
1773
            pf.bbits = 5;
1774
            break;
1775
        case 16:
1776
            pf.rmask = 0x0000F800;
1777
            pf.gmask = 0x000007E0;
1778
            pf.bmask = 0x0000001F;
1779
            pf.rmax = 31;
1780
            pf.gmax = 63;
1781
            pf.bmax = 31;
1782
            pf.rshift = 11;
1783
            pf.gshift = 5;
1784
            pf.bshift = 0;
1785
            pf.rbits = 5;
1786
            pf.gbits = 6;
1787
            pf.bbits = 5;
1788
            break;
1789
        case 24:
1790
            pf.rmask = 0x00FF0000;
1791
            pf.gmask = 0x0000FF00;
1792
            pf.bmask = 0x000000FF;
1793
            pf.rmax = 255;
1794
            pf.gmax = 255;
1795
            pf.bmax = 255;
1796
            pf.rshift = 16;
1797
            pf.gshift = 8;
1798
            pf.bshift = 0;
1799
            pf.rbits = 8;
1800
            pf.gbits = 8;
1801
            pf.bbits = 8;
1802
            break;
1803
        case 32:
1804
            pf.rmask = 0x00FF0000;
1805
            pf.gmask = 0x0000FF00;
1806
            pf.bmask = 0x000000FF;
1807
            pf.rmax = 255;
1808
            pf.gmax = 255;
1809
            pf.bmax = 255;
1810
            pf.rshift = 16;
1811
            pf.gshift = 8;
1812
            pf.bshift = 0;
1813
            pf.rbits = 8;
1814
            pf.gbits = 8;
1815
            pf.bbits = 8;
1816
            break;
1817
        default:
1818
            break;
1819
    }
1820
    return pf;
1821
}
1822

    
1823
static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
1824
                              Error **errp)
1825
{
1826
    int val;
1827

    
1828
    backend->vc = g_new0(ChardevVC, 1);
1829

    
1830
    val = qemu_opt_get_number(opts, "width", 0);
1831
    if (val != 0) {
1832
        backend->vc->has_width = true;
1833
        backend->vc->width = val;
1834
    }
1835

    
1836
    val = qemu_opt_get_number(opts, "height", 0);
1837
    if (val != 0) {
1838
        backend->vc->has_height = true;
1839
        backend->vc->height = val;
1840
    }
1841

    
1842
    val = qemu_opt_get_number(opts, "cols", 0);
1843
    if (val != 0) {
1844
        backend->vc->has_cols = true;
1845
        backend->vc->cols = val;
1846
    }
1847

    
1848
    val = qemu_opt_get_number(opts, "rows", 0);
1849
    if (val != 0) {
1850
        backend->vc->has_rows = true;
1851
        backend->vc->rows = val;
1852
    }
1853
}
1854

    
1855
static void register_types(void)
1856
{
1857
    register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
1858
                              qemu_chr_parse_vc);
1859
}
1860

    
1861
type_init(register_types);