Statistics
| Branch: | Revision:

root / ui / console.c @ dea1b0bd

History | View | Annotate | Download (50.3 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
typedef struct TextAttributes {
36
    uint8_t fgcol:4;
37
    uint8_t bgcol:4;
38
    uint8_t bold:1;
39
    uint8_t uline:1;
40
    uint8_t blink:1;
41
    uint8_t invers:1;
42
    uint8_t unvisible:1;
43
} TextAttributes;
44

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

    
50
#define MAX_ESC_PARAMS 3
51

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

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

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

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

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

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

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

    
115
struct QemuConsole {
116
    int index;
117
    console_type_t console_type;
118
    DisplayState *ds;
119
    DisplaySurface *surface;
120

    
121
    /* Graphic console state.  */
122
    const GraphicHwOps *hw_ops;
123
    void *hw;
124

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

    
142
    int update_x0;
143
    int update_y0;
144
    int update_x1;
145
    int update_y1;
146

    
147
    enum TTYState state;
148
    int esc_params[MAX_ESC_PARAMS];
149
    int nb_esc_params;
150

    
151
    CharDriverState *chr;
152
    /* fifo for key pressed */
153
    QEMUFIFO out_fifo;
154
    uint8_t out_fifo_buf[16];
155
    QEMUTimer *kbd_timer;
156
};
157

    
158
struct DisplayState {
159
    struct QEMUTimer *gui_timer;
160
    uint64_t last_update;
161
    uint64_t update_interval;
162
    bool refreshing;
163
    bool have_gfx;
164
    bool have_text;
165

    
166
    QLIST_HEAD(, DisplayChangeListener) listeners;
167
};
168

    
169
static DisplayState *display_state;
170
static QemuConsole *active_console;
171
static QemuConsole *consoles[MAX_CONSOLES];
172
static int nb_consoles = 0;
173

    
174
static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
175
static void dpy_gfx_switch_surface(DisplayState *ds,
176
                                   DisplaySurface *surface);
177
static void dpy_refresh(DisplayState *s);
178

    
179
static void gui_update(void *opaque)
180
{
181
    uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
182
    uint64_t dcl_interval;
183
    DisplayState *ds = opaque;
184
    DisplayChangeListener *dcl;
185
    int i;
186

    
187
    ds->refreshing = true;
188
    dpy_refresh(ds);
189
    ds->refreshing = false;
190

    
191
    QLIST_FOREACH(dcl, &ds->listeners, next) {
192
        dcl_interval = dcl->update_interval ?
193
            dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
194
        if (interval > dcl_interval) {
195
            interval = dcl_interval;
196
        }
197
    }
198
    if (ds->update_interval != interval) {
199
        ds->update_interval = interval;
200
        for (i = 0; i < nb_consoles; i++) {
201
            if (consoles[i]->hw_ops->update_interval) {
202
                consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval);
203
            }
204
        }
205
        trace_console_refresh(interval);
206
    }
207
    ds->last_update = qemu_get_clock_ms(rt_clock);
208
    qemu_mod_timer(ds->gui_timer, ds->last_update + interval);
209
}
210

    
211
static void gui_setup_refresh(DisplayState *ds)
212
{
213
    DisplayChangeListener *dcl;
214
    bool need_timer = false;
215
    bool have_gfx = false;
216
    bool have_text = false;
217

    
218
    QLIST_FOREACH(dcl, &ds->listeners, next) {
219
        if (dcl->ops->dpy_refresh != NULL) {
220
            need_timer = true;
221
        }
222
        if (dcl->ops->dpy_gfx_update != NULL) {
223
            have_gfx = true;
224
        }
225
        if (dcl->ops->dpy_text_update != NULL) {
226
            have_text = true;
227
        }
228
    }
229

    
230
    if (need_timer && ds->gui_timer == NULL) {
231
        ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
232
        qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
233
    }
234
    if (!need_timer && ds->gui_timer != NULL) {
235
        qemu_del_timer(ds->gui_timer);
236
        qemu_free_timer(ds->gui_timer);
237
        ds->gui_timer = NULL;
238
    }
239

    
240
    ds->have_gfx = have_gfx;
241
    ds->have_text = have_text;
242
}
243

    
244
void graphic_hw_update(QemuConsole *con)
245
{
246
    if (!con) {
247
        con = active_console;
248
    }
249
    if (con && con->hw_ops->gfx_update) {
250
        con->hw_ops->gfx_update(con->hw);
251
    }
252
}
253

    
254
void graphic_hw_invalidate(QemuConsole *con)
255
{
256
    if (!con) {
257
        con = active_console;
258
    }
259
    if (con && con->hw_ops->invalidate) {
260
        con->hw_ops->invalidate(con->hw);
261
    }
262
}
263

    
264
static void ppm_save(const char *filename, struct DisplaySurface *ds,
265
                     Error **errp)
266
{
267
    int width = pixman_image_get_width(ds->image);
268
    int height = pixman_image_get_height(ds->image);
269
    FILE *f;
270
    int y;
271
    int ret;
272
    pixman_image_t *linebuf;
273

    
274
    trace_ppm_save(filename, ds);
275
    f = fopen(filename, "wb");
276
    if (!f) {
277
        error_setg(errp, "failed to open file '%s': %s", filename,
278
                   strerror(errno));
279
        return;
280
    }
281
    ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
282
    if (ret < 0) {
283
        linebuf = NULL;
284
        goto write_err;
285
    }
286
    linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
287
    for (y = 0; y < height; y++) {
288
        qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
289
        clearerr(f);
290
        ret = fwrite(pixman_image_get_data(linebuf), 1,
291
                     pixman_image_get_stride(linebuf), f);
292
        (void)ret;
293
        if (ferror(f)) {
294
            goto write_err;
295
        }
296
    }
297

    
298
out:
299
    qemu_pixman_image_unref(linebuf);
300
    fclose(f);
301
    return;
302

    
303
write_err:
304
    error_setg(errp, "failed to write to file '%s': %s", filename,
305
               strerror(errno));
306
    unlink(filename);
307
    goto out;
308
}
309

    
310
void qmp_screendump(const char *filename, Error **errp)
311
{
312
    QemuConsole *con = consoles[0];
313
    DisplaySurface *surface;
314

    
315
    if (con == NULL) {
316
        error_setg(errp, "There is no QemuConsole I can screendump from.");
317
        return;
318
    }
319

    
320
    graphic_hw_update(con);
321
    surface = qemu_console_surface(con);
322
    ppm_save(filename, surface, errp);
323
}
324

    
325
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
326
{
327
    if (!con) {
328
        con = active_console;
329
    }
330
    if (con && con->hw_ops->text_update) {
331
        con->hw_ops->text_update(con->hw, chardata);
332
    }
333
}
334

    
335
static void vga_fill_rect(QemuConsole *con,
336
                          int posx, int posy, int width, int height,
337
                          pixman_color_t color)
338
{
339
    DisplaySurface *surface = qemu_console_surface(con);
340
    pixman_rectangle16_t rect = {
341
        .x = posx, .y = posy, .width = width, .height = height
342
    };
343

    
344
    pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
345
                                 &color, 1, &rect);
346
}
347

    
348
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
349
static void vga_bitblt(QemuConsole *con,
350
                       int xs, int ys, int xd, int yd, int w, int h)
351
{
352
    DisplaySurface *surface = qemu_console_surface(con);
353

    
354
    pixman_image_composite(PIXMAN_OP_SRC,
355
                           surface->image, NULL, surface->image,
356
                           xs, ys, 0, 0, xd, yd, w, h);
357
}
358

    
359
/***********************************************************/
360
/* basic char display */
361

    
362
#define FONT_HEIGHT 16
363
#define FONT_WIDTH 8
364

    
365
#include "vgafont.h"
366

    
367
#ifndef CONFIG_CURSES
368
enum color_names {
369
    COLOR_BLACK   = 0,
370
    COLOR_RED     = 1,
371
    COLOR_GREEN   = 2,
372
    COLOR_YELLOW  = 3,
373
    COLOR_BLUE    = 4,
374
    COLOR_MAGENTA = 5,
375
    COLOR_CYAN    = 6,
376
    COLOR_WHITE   = 7
377
};
378
#endif
379

    
380
#define QEMU_RGB(r, g, b)                                               \
381
    { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
382

    
383
static const pixman_color_t color_table_rgb[2][8] = {
384
    {   /* dark */
385
        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
386
        QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
387
        QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
388
        QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
389
        QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
390
        QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
391
        QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
392
        QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
393
    },
394
    {   /* bright */
395
        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
396
        QEMU_RGB(0xff, 0x00, 0x00),  /* red */
397
        QEMU_RGB(0x00, 0xff, 0x00),  /* green */
398
        QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
399
        QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
400
        QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
401
        QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
402
        QEMU_RGB(0xff, 0xff, 0xff),  /* white */
403
    }
404
};
405

    
406
#ifdef DEBUG_CONSOLE
407
static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
408
{
409
    if (t_attrib->bold) {
410
        printf("b");
411
    } else {
412
        printf(" ");
413
    }
414
    if (t_attrib->uline) {
415
        printf("u");
416
    } else {
417
        printf(" ");
418
    }
419
    if (t_attrib->blink) {
420
        printf("l");
421
    } else {
422
        printf(" ");
423
    }
424
    if (t_attrib->invers) {
425
        printf("i");
426
    } else {
427
        printf(" ");
428
    }
429
    if (t_attrib->unvisible) {
430
        printf("n");
431
    } else {
432
        printf(" ");
433
    }
434

    
435
    printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
436
}
437
#endif
438

    
439
static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
440
                          TextAttributes *t_attrib)
441
{
442
    static pixman_image_t *glyphs[256];
443
    DisplaySurface *surface = qemu_console_surface(s);
444
    pixman_color_t fgcol, bgcol;
445

    
446
    if (t_attrib->invers) {
447
        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
448
        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
449
    } else {
450
        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
451
        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
452
    }
453

    
454
    if (!glyphs[ch]) {
455
        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
456
    }
457
    qemu_pixman_glyph_render(glyphs[ch], surface->image,
458
                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
459
}
460

    
461
static void text_console_resize(QemuConsole *s)
462
{
463
    TextCell *cells, *c, *c1;
464
    int w1, x, y, last_width;
465

    
466
    last_width = s->width;
467
    s->width = surface_width(s->surface) / FONT_WIDTH;
468
    s->height = surface_height(s->surface) / FONT_HEIGHT;
469

    
470
    w1 = last_width;
471
    if (s->width < w1)
472
        w1 = s->width;
473

    
474
    cells = g_malloc(s->width * s->total_height * sizeof(TextCell));
475
    for(y = 0; y < s->total_height; y++) {
476
        c = &cells[y * s->width];
477
        if (w1 > 0) {
478
            c1 = &s->cells[y * last_width];
479
            for(x = 0; x < w1; x++) {
480
                *c++ = *c1++;
481
            }
482
        }
483
        for(x = w1; x < s->width; x++) {
484
            c->ch = ' ';
485
            c->t_attrib = s->t_attrib_default;
486
            c++;
487
        }
488
    }
489
    g_free(s->cells);
490
    s->cells = cells;
491
}
492

    
493
static inline void text_update_xy(QemuConsole *s, int x, int y)
494
{
495
    s->text_x[0] = MIN(s->text_x[0], x);
496
    s->text_x[1] = MAX(s->text_x[1], x);
497
    s->text_y[0] = MIN(s->text_y[0], y);
498
    s->text_y[1] = MAX(s->text_y[1], y);
499
}
500

    
501
static void invalidate_xy(QemuConsole *s, int x, int y)
502
{
503
    if (s->update_x0 > x * FONT_WIDTH)
504
        s->update_x0 = x * FONT_WIDTH;
505
    if (s->update_y0 > y * FONT_HEIGHT)
506
        s->update_y0 = y * FONT_HEIGHT;
507
    if (s->update_x1 < (x + 1) * FONT_WIDTH)
508
        s->update_x1 = (x + 1) * FONT_WIDTH;
509
    if (s->update_y1 < (y + 1) * FONT_HEIGHT)
510
        s->update_y1 = (y + 1) * FONT_HEIGHT;
511
}
512

    
513
static void update_xy(QemuConsole *s, int x, int y)
514
{
515
    TextCell *c;
516
    int y1, y2;
517

    
518
    if (s != active_console) {
519
        return;
520
    }
521

    
522
    if (s->ds->have_text) {
523
        text_update_xy(s, x, y);
524
    }
525

    
526
    if (s->ds->have_gfx) {
527
        y1 = (s->y_base + y) % s->total_height;
528
        y2 = y1 - s->y_displayed;
529
        if (y2 < 0)
530
            y2 += s->total_height;
531
        if (y2 < s->height) {
532
            c = &s->cells[y1 * s->width + x];
533
            vga_putcharxy(s, x, y2, c->ch,
534
                          &(c->t_attrib));
535
            invalidate_xy(s, x, y2);
536
        }
537
    }
538
}
539

    
540
static void console_show_cursor(QemuConsole *s, int show)
541
{
542
    TextCell *c;
543
    int y, y1;
544
    int x = s->x;
545

    
546
    if (s != active_console) {
547
        return;
548
    }
549

    
550
    if (s->ds->have_text) {
551
        s->cursor_invalidate = 1;
552
    }
553

    
554
    if (s->ds->have_gfx) {
555
        if (x >= s->width) {
556
            x = s->width - 1;
557
        }
558
        y1 = (s->y_base + s->y) % s->total_height;
559
        y = y1 - s->y_displayed;
560
        if (y < 0)
561
            y += s->total_height;
562
        if (y < s->height) {
563
            c = &s->cells[y1 * s->width + x];
564
            if (show && s->cursor_visible_phase) {
565
                TextAttributes t_attrib = s->t_attrib_default;
566
                t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
567
                vga_putcharxy(s, x, y, c->ch, &t_attrib);
568
            } else {
569
                vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
570
            }
571
            invalidate_xy(s, x, y);
572
        }
573
    }
574
}
575

    
576
static void console_refresh(QemuConsole *s)
577
{
578
    DisplaySurface *surface = qemu_console_surface(s);
579
    TextCell *c;
580
    int x, y, y1;
581

    
582
    if (s != active_console)
583
        return;
584

    
585
    if (s->ds->have_text) {
586
        s->text_x[0] = 0;
587
        s->text_y[0] = 0;
588
        s->text_x[1] = s->width - 1;
589
        s->text_y[1] = s->height - 1;
590
        s->cursor_invalidate = 1;
591
    }
592

    
593
    if (s->ds->have_gfx) {
594
        vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
595
                      color_table_rgb[0][COLOR_BLACK]);
596
        y1 = s->y_displayed;
597
        for (y = 0; y < s->height; y++) {
598
            c = s->cells + y1 * s->width;
599
            for (x = 0; x < s->width; x++) {
600
                vga_putcharxy(s, x, y, c->ch,
601
                              &(c->t_attrib));
602
                c++;
603
            }
604
            if (++y1 == s->total_height) {
605
                y1 = 0;
606
            }
607
        }
608
        console_show_cursor(s, 1);
609
        dpy_gfx_update(s, 0, 0,
610
                       surface_width(surface), surface_height(surface));
611
    }
612
}
613

    
614
static void console_scroll(int ydelta)
615
{
616
    QemuConsole *s;
617
    int i, y1;
618

    
619
    s = active_console;
620
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
621
        return;
622

    
623
    if (ydelta > 0) {
624
        for(i = 0; i < ydelta; i++) {
625
            if (s->y_displayed == s->y_base)
626
                break;
627
            if (++s->y_displayed == s->total_height)
628
                s->y_displayed = 0;
629
        }
630
    } else {
631
        ydelta = -ydelta;
632
        i = s->backscroll_height;
633
        if (i > s->total_height - s->height)
634
            i = s->total_height - s->height;
635
        y1 = s->y_base - i;
636
        if (y1 < 0)
637
            y1 += s->total_height;
638
        for(i = 0; i < ydelta; i++) {
639
            if (s->y_displayed == y1)
640
                break;
641
            if (--s->y_displayed < 0)
642
                s->y_displayed = s->total_height - 1;
643
        }
644
    }
645
    console_refresh(s);
646
}
647

    
648
static void console_put_lf(QemuConsole *s)
649
{
650
    TextCell *c;
651
    int x, y1;
652

    
653
    s->y++;
654
    if (s->y >= s->height) {
655
        s->y = s->height - 1;
656

    
657
        if (s->y_displayed == s->y_base) {
658
            if (++s->y_displayed == s->total_height)
659
                s->y_displayed = 0;
660
        }
661
        if (++s->y_base == s->total_height)
662
            s->y_base = 0;
663
        if (s->backscroll_height < s->total_height)
664
            s->backscroll_height++;
665
        y1 = (s->y_base + s->height - 1) % s->total_height;
666
        c = &s->cells[y1 * s->width];
667
        for(x = 0; x < s->width; x++) {
668
            c->ch = ' ';
669
            c->t_attrib = s->t_attrib_default;
670
            c++;
671
        }
672
        if (s == active_console && s->y_displayed == s->y_base) {
673
            if (s->ds->have_text) {
674
                s->text_x[0] = 0;
675
                s->text_y[0] = 0;
676
                s->text_x[1] = s->width - 1;
677
                s->text_y[1] = s->height - 1;
678
            }
679

    
680
            if (s->ds->have_gfx) {
681
                vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
682
                           s->width * FONT_WIDTH,
683
                           (s->height - 1) * FONT_HEIGHT);
684
                vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
685
                              s->width * FONT_WIDTH, FONT_HEIGHT,
686
                              color_table_rgb[0][s->t_attrib_default.bgcol]);
687
                s->update_x0 = 0;
688
                s->update_y0 = 0;
689
                s->update_x1 = s->width * FONT_WIDTH;
690
                s->update_y1 = s->height * FONT_HEIGHT;
691
            }
692
        }
693
    }
694
}
695

    
696
/* Set console attributes depending on the current escape codes.
697
 * NOTE: I know this code is not very efficient (checking every color for it
698
 * self) but it is more readable and better maintainable.
699
 */
700
static void console_handle_escape(QemuConsole *s)
701
{
702
    int i;
703

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

    
793
static void console_clear_xy(QemuConsole *s, int x, int y)
794
{
795
    int y1 = (s->y_base + y) % s->total_height;
796
    TextCell *c = &s->cells[y1 * s->width + x];
797
    c->ch = ' ';
798
    c->t_attrib = s->t_attrib_default;
799
    update_xy(s, x, y);
800
}
801

    
802
/* set cursor, checking bounds */
803
static void set_cursor(QemuConsole *s, int x, int y)
804
{
805
    if (x < 0) {
806
        x = 0;
807
    }
808
    if (y < 0) {
809
        y = 0;
810
    }
811
    if (y >= s->height) {
812
        y = s->height - 1;
813
    }
814
    if (x >= s->width) {
815
        x = s->width - 1;
816
    }
817

    
818
    s->x = x;
819
    s->y = y;
820
}
821

    
822
static void console_putchar(QemuConsole *s, int ch)
823
{
824
    TextCell *c;
825
    int y1, i;
826
    int x, y;
827

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

    
892
                *param = (*param <= (INT_MAX - digit) / 10) ?
893
                         *param * 10 + digit : INT_MAX;
894
            }
895
        } else {
896
            if (s->nb_esc_params < MAX_ESC_PARAMS)
897
                s->nb_esc_params++;
898
            if (ch == ';')
899
                break;
900
#ifdef DEBUG_CONSOLE
901
            fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
902
                    s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
903
#endif
904
            s->state = TTY_STATE_NORM;
905
            switch(ch) {
906
            case 'A':
907
                /* move cursor up */
908
                if (s->esc_params[0] == 0) {
909
                    s->esc_params[0] = 1;
910
                }
911
                set_cursor(s, s->x, s->y - s->esc_params[0]);
912
                break;
913
            case 'B':
914
                /* move cursor down */
915
                if (s->esc_params[0] == 0) {
916
                    s->esc_params[0] = 1;
917
                }
918
                set_cursor(s, s->x, s->y + s->esc_params[0]);
919
                break;
920
            case 'C':
921
                /* move cursor right */
922
                if (s->esc_params[0] == 0) {
923
                    s->esc_params[0] = 1;
924
                }
925
                set_cursor(s, s->x + s->esc_params[0], s->y);
926
                break;
927
            case 'D':
928
                /* move cursor left */
929
                if (s->esc_params[0] == 0) {
930
                    s->esc_params[0] = 1;
931
                }
932
                set_cursor(s, s->x - s->esc_params[0], s->y);
933
                break;
934
            case 'G':
935
                /* move cursor to column */
936
                set_cursor(s, s->esc_params[0] - 1, s->y);
937
                break;
938
            case 'f':
939
            case 'H':
940
                /* move cursor to row, column */
941
                set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
942
                break;
943
            case 'J':
944
                switch (s->esc_params[0]) {
945
                case 0:
946
                    /* clear to end of screen */
947
                    for (y = s->y; y < s->height; y++) {
948
                        for (x = 0; x < s->width; x++) {
949
                            if (y == s->y && x < s->x) {
950
                                continue;
951
                            }
952
                            console_clear_xy(s, x, y);
953
                        }
954
                    }
955
                    break;
956
                case 1:
957
                    /* clear from beginning of screen */
958
                    for (y = 0; y <= s->y; y++) {
959
                        for (x = 0; x < s->width; x++) {
960
                            if (y == s->y && x > s->x) {
961
                                break;
962
                            }
963
                            console_clear_xy(s, x, y);
964
                        }
965
                    }
966
                    break;
967
                case 2:
968
                    /* clear entire screen */
969
                    for (y = 0; y <= s->height; y++) {
970
                        for (x = 0; x < s->width; x++) {
971
                            console_clear_xy(s, x, y);
972
                        }
973
                    }
974
                    break;
975
                }
976
                break;
977
            case 'K':
978
                switch (s->esc_params[0]) {
979
                case 0:
980
                    /* clear to eol */
981
                    for(x = s->x; x < s->width; x++) {
982
                        console_clear_xy(s, x, s->y);
983
                    }
984
                    break;
985
                case 1:
986
                    /* clear from beginning of line */
987
                    for (x = 0; x <= s->x; x++) {
988
                        console_clear_xy(s, x, s->y);
989
                    }
990
                    break;
991
                case 2:
992
                    /* clear entire line */
993
                    for(x = 0; x < s->width; x++) {
994
                        console_clear_xy(s, x, s->y);
995
                    }
996
                    break;
997
                }
998
                break;
999
            case 'm':
1000
                console_handle_escape(s);
1001
                break;
1002
            case 'n':
1003
                /* report cursor position */
1004
                /* TODO: send ESC[row;colR */
1005
                break;
1006
            case 's':
1007
                /* save cursor position */
1008
                s->x_saved = s->x;
1009
                s->y_saved = s->y;
1010
                break;
1011
            case 'u':
1012
                /* restore cursor position */
1013
                s->x = s->x_saved;
1014
                s->y = s->y_saved;
1015
                break;
1016
            default:
1017
#ifdef DEBUG_CONSOLE
1018
                fprintf(stderr, "unhandled escape character '%c'\n", ch);
1019
#endif
1020
                break;
1021
            }
1022
            break;
1023
        }
1024
    }
1025
}
1026

    
1027
void console_select(unsigned int index)
1028
{
1029
    QemuConsole *s;
1030

    
1031
    if (index >= MAX_CONSOLES)
1032
        return;
1033

    
1034
    trace_console_select(index);
1035
    s = consoles[index];
1036
    if (s) {
1037
        DisplayState *ds = s->ds;
1038

    
1039
        if (active_console && active_console->cursor_timer) {
1040
            qemu_del_timer(active_console->cursor_timer);
1041
        }
1042
        active_console = s;
1043
        if (ds->have_gfx) {
1044
            dpy_gfx_switch_surface(ds, s->surface);
1045
            dpy_gfx_update(s, 0, 0, surface_width(s->surface),
1046
                           surface_height(s->surface));
1047
        }
1048
        if (ds->have_text) {
1049
            dpy_text_resize(s, s->width, s->height);
1050
        }
1051
        if (s->cursor_timer) {
1052
            qemu_mod_timer(s->cursor_timer,
1053
                   qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1054
        }
1055
    }
1056
}
1057

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

    
1063
    s->update_x0 = s->width * FONT_WIDTH;
1064
    s->update_y0 = s->height * FONT_HEIGHT;
1065
    s->update_x1 = 0;
1066
    s->update_y1 = 0;
1067
    console_show_cursor(s, 0);
1068
    for(i = 0; i < len; i++) {
1069
        console_putchar(s, buf[i]);
1070
    }
1071
    console_show_cursor(s, 1);
1072
    if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
1073
        dpy_gfx_update(s, s->update_x0, s->update_y0,
1074
                       s->update_x1 - s->update_x0,
1075
                       s->update_y1 - s->update_y0);
1076
    }
1077
    return len;
1078
}
1079

    
1080
static void kbd_send_chars(void *opaque)
1081
{
1082
    QemuConsole *s = opaque;
1083
    int len;
1084
    uint8_t buf[16];
1085

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

    
1102
/* called when an ascii key is pressed */
1103
void kbd_put_keysym(int keysym)
1104
{
1105
    QemuConsole *s;
1106
    uint8_t buf[16], *q;
1107
    int c;
1108

    
1109
    s = active_console;
1110
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
1111
        return;
1112

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

    
1158
static void text_console_invalidate(void *opaque)
1159
{
1160
    QemuConsole *s = (QemuConsole *) opaque;
1161

    
1162
    if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1163
        text_console_resize(s);
1164
    }
1165
    console_refresh(s);
1166
}
1167

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

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

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

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

    
1227
static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1228
                               int linesize, PixelFormat pf, int newflags)
1229
{
1230
    surface->pf = pf;
1231

    
1232
    qemu_pixman_image_unref(surface->image);
1233
    surface->image = NULL;
1234

    
1235
    surface->format = qemu_pixman_get_format(&pf);
1236
    assert(surface->format != 0);
1237
    surface->image = pixman_image_create_bits(surface->format,
1238
                                              width, height,
1239
                                              NULL, linesize);
1240
    assert(surface->image != NULL);
1241

    
1242
    surface->flags = newflags | QEMU_ALLOCATED_FLAG;
1243
#ifdef HOST_WORDS_BIGENDIAN
1244
    surface->flags |= QEMU_BIG_ENDIAN_FLAG;
1245
#endif
1246
}
1247

    
1248
DisplaySurface *qemu_create_displaysurface(int width, int height)
1249
{
1250
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1251
    int linesize = width * 4;
1252

    
1253
    trace_displaysurface_create(surface, width, height);
1254
    qemu_alloc_display(surface, width, height, linesize,
1255
                       qemu_default_pixelformat(32), 0);
1256
    return surface;
1257
}
1258

    
1259
DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
1260
                                                int linesize, uint8_t *data,
1261
                                                bool byteswap)
1262
{
1263
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1264

    
1265
    trace_displaysurface_create_from(surface, width, height, bpp, byteswap);
1266
    if (byteswap) {
1267
        surface->pf = qemu_different_endianness_pixelformat(bpp);
1268
    } else {
1269
        surface->pf = qemu_default_pixelformat(bpp);
1270
    }
1271

    
1272
    surface->format = qemu_pixman_get_format(&surface->pf);
1273
    assert(surface->format != 0);
1274
    surface->image = pixman_image_create_bits(surface->format,
1275
                                              width, height,
1276
                                              (void *)data, linesize);
1277
    assert(surface->image != NULL);
1278

    
1279
#ifdef HOST_WORDS_BIGENDIAN
1280
    surface->flags = QEMU_BIG_ENDIAN_FLAG;
1281
#endif
1282

    
1283
    return surface;
1284
}
1285

    
1286
void qemu_free_displaysurface(DisplaySurface *surface)
1287
{
1288
    if (surface == NULL) {
1289
        return;
1290
    }
1291
    trace_displaysurface_free(surface);
1292
    qemu_pixman_image_unref(surface->image);
1293
    g_free(surface);
1294
}
1295

    
1296
void register_displaychangelistener(DisplayState *ds,
1297
                                    DisplayChangeListener *dcl)
1298
{
1299
    trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1300
    dcl->ds = ds;
1301
    QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
1302
    gui_setup_refresh(ds);
1303
    if (dcl->ops->dpy_gfx_switch && active_console) {
1304
        dcl->ops->dpy_gfx_switch(dcl, active_console->surface);
1305
    }
1306
}
1307

    
1308
void update_displaychangelistener(DisplayChangeListener *dcl,
1309
                                  uint64_t interval)
1310
{
1311
    DisplayState *ds = dcl->ds;
1312

    
1313
    dcl->update_interval = interval;
1314
    if (!ds->refreshing && ds->update_interval > interval) {
1315
        qemu_mod_timer(ds->gui_timer, ds->last_update + interval);
1316
    }
1317
}
1318

    
1319
void unregister_displaychangelistener(DisplayChangeListener *dcl)
1320
{
1321
    DisplayState *ds = dcl->ds;
1322
    trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1323
    QLIST_REMOVE(dcl, next);
1324
    gui_setup_refresh(ds);
1325
}
1326

    
1327
void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1328
{
1329
    DisplayState *s = con->ds;
1330
    struct DisplayChangeListener *dcl;
1331
    int width = surface_width(con->surface);
1332
    int height = surface_height(con->surface);
1333

    
1334
    x = MAX(x, 0);
1335
    y = MAX(y, 0);
1336
    x = MIN(x, width);
1337
    y = MIN(y, height);
1338
    w = MIN(w, width - x);
1339
    h = MIN(h, height - y);
1340

    
1341
    if (con != active_console) {
1342
        return;
1343
    }
1344
    QLIST_FOREACH(dcl, &s->listeners, next) {
1345
        if (dcl->ops->dpy_gfx_update) {
1346
            dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1347
        }
1348
    }
1349
}
1350

    
1351
static void dpy_gfx_switch_surface(DisplayState *ds,
1352
                                   DisplaySurface *surface)
1353
{
1354
    struct DisplayChangeListener *dcl;
1355

    
1356
    QLIST_FOREACH(dcl, &ds->listeners, next) {
1357
        if (dcl->ops->dpy_gfx_switch) {
1358
            dcl->ops->dpy_gfx_switch(dcl, surface);
1359
        }
1360
    }
1361
}
1362

    
1363
void dpy_gfx_replace_surface(QemuConsole *con,
1364
                             DisplaySurface *surface)
1365
{
1366
    DisplayState *s = con->ds;
1367
    DisplaySurface *old_surface = con->surface;
1368

    
1369
    con->surface = surface;
1370
    if (con == active_console) {
1371
        dpy_gfx_switch_surface(s, surface);
1372
    }
1373
    qemu_free_displaysurface(old_surface);
1374
}
1375

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

    
1386
void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
1387
                  int dst_x, int dst_y, int w, int h)
1388
{
1389
    DisplayState *s = con->ds;
1390
    struct DisplayChangeListener *dcl;
1391

    
1392
    if (con != active_console) {
1393
        return;
1394
    }
1395
    QLIST_FOREACH(dcl, &s->listeners, next) {
1396
        if (dcl->ops->dpy_gfx_copy) {
1397
            dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
1398
        } else { /* TODO */
1399
            dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
1400
        }
1401
    }
1402
}
1403

    
1404
void dpy_text_cursor(QemuConsole *con, int x, int y)
1405
{
1406
    DisplayState *s = con->ds;
1407
    struct DisplayChangeListener *dcl;
1408

    
1409
    if (con != active_console) {
1410
        return;
1411
    }
1412
    QLIST_FOREACH(dcl, &s->listeners, next) {
1413
        if (dcl->ops->dpy_text_cursor) {
1414
            dcl->ops->dpy_text_cursor(dcl, x, y);
1415
        }
1416
    }
1417
}
1418

    
1419
void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1420
{
1421
    DisplayState *s = con->ds;
1422
    struct DisplayChangeListener *dcl;
1423

    
1424
    if (con != active_console) {
1425
        return;
1426
    }
1427
    QLIST_FOREACH(dcl, &s->listeners, next) {
1428
        if (dcl->ops->dpy_text_update) {
1429
            dcl->ops->dpy_text_update(dcl, x, y, w, h);
1430
        }
1431
    }
1432
}
1433

    
1434
void dpy_text_resize(QemuConsole *con, int w, int h)
1435
{
1436
    DisplayState *s = con->ds;
1437
    struct DisplayChangeListener *dcl;
1438

    
1439
    if (con != active_console) {
1440
        return;
1441
    }
1442
    QLIST_FOREACH(dcl, &s->listeners, next) {
1443
        if (dcl->ops->dpy_text_resize) {
1444
            dcl->ops->dpy_text_resize(dcl, w, h);
1445
        }
1446
    }
1447
}
1448

    
1449
void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1450
{
1451
    DisplayState *s = con->ds;
1452
    struct DisplayChangeListener *dcl;
1453

    
1454
    if (con != active_console) {
1455
        return;
1456
    }
1457
    QLIST_FOREACH(dcl, &s->listeners, next) {
1458
        if (dcl->ops->dpy_mouse_set) {
1459
            dcl->ops->dpy_mouse_set(dcl, x, y, on);
1460
        }
1461
    }
1462
}
1463

    
1464
void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1465
{
1466
    DisplayState *s = con->ds;
1467
    struct DisplayChangeListener *dcl;
1468

    
1469
    if (con != active_console) {
1470
        return;
1471
    }
1472
    QLIST_FOREACH(dcl, &s->listeners, next) {
1473
        if (dcl->ops->dpy_cursor_define) {
1474
            dcl->ops->dpy_cursor_define(dcl, cursor);
1475
        }
1476
    }
1477
}
1478

    
1479
bool dpy_cursor_define_supported(QemuConsole *con)
1480
{
1481
    DisplayState *s = con->ds;
1482
    struct DisplayChangeListener *dcl;
1483
    QLIST_FOREACH(dcl, &s->listeners, next) {
1484
        if (dcl->ops->dpy_cursor_define) {
1485
            return true;
1486
        }
1487
    }
1488
    return false;
1489
}
1490

    
1491
/***********************************************************/
1492
/* register display */
1493

    
1494
/* console.c internal use only */
1495
static DisplayState *get_alloc_displaystate(void)
1496
{
1497
    if (!display_state) {
1498
        display_state = g_new0(DisplayState, 1);
1499
    }
1500
    return display_state;
1501
}
1502

    
1503
/*
1504
 * Called by main(), after creating QemuConsoles
1505
 * and before initializing ui (sdl/vnc/...).
1506
 */
1507
DisplayState *init_displaystate(void)
1508
{
1509
    int i;
1510

    
1511
    if (!display_state) {
1512
        display_state = g_new0(DisplayState, 1);
1513
    }
1514

    
1515
    for (i = 0; i < nb_consoles; i++) {
1516
        if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
1517
            consoles[i]->ds == NULL) {
1518
            text_console_do_init(consoles[i]->chr, display_state);
1519
        }
1520
    }
1521

    
1522
    return display_state;
1523
}
1524

    
1525
QemuConsole *graphic_console_init(const GraphicHwOps *hw_ops,
1526
                                  void *opaque)
1527
{
1528
    int width = 640;
1529
    int height = 480;
1530
    QemuConsole *s;
1531
    DisplayState *ds;
1532

    
1533
    ds = get_alloc_displaystate();
1534
    trace_console_gfx_new();
1535
    s = new_console(ds, GRAPHIC_CONSOLE);
1536
    s->hw_ops = hw_ops;
1537
    s->hw = opaque;
1538

    
1539
    s->surface = qemu_create_displaysurface(width, height);
1540
    return s;
1541
}
1542

    
1543
int is_graphic_console(void)
1544
{
1545
    return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1546
}
1547

    
1548
int is_fixedsize_console(void)
1549
{
1550
    return active_console && active_console->console_type != TEXT_CONSOLE;
1551
}
1552

    
1553
static void text_console_set_echo(CharDriverState *chr, bool echo)
1554
{
1555
    QemuConsole *s = chr->opaque;
1556

    
1557
    s->echo = echo;
1558
}
1559

    
1560
static void text_console_update_cursor(void *opaque)
1561
{
1562
    QemuConsole *s = opaque;
1563

    
1564
    s->cursor_visible_phase = !s->cursor_visible_phase;
1565
    graphic_hw_invalidate(s);
1566
    qemu_mod_timer(s->cursor_timer,
1567
                   qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1568
}
1569

    
1570
static const GraphicHwOps text_console_ops = {
1571
    .invalidate  = text_console_invalidate,
1572
    .text_update = text_console_update,
1573
};
1574

    
1575
static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
1576
{
1577
    QemuConsole *s;
1578
    int g_width = 80 * FONT_WIDTH;
1579
    int g_height = 24 * FONT_HEIGHT;
1580

    
1581
    s = chr->opaque;
1582

    
1583
    chr->chr_write = console_puts;
1584

    
1585
    s->out_fifo.buf = s->out_fifo_buf;
1586
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1587
    s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
1588
    s->ds = ds;
1589

    
1590
    s->y_displayed = 0;
1591
    s->y_base = 0;
1592
    s->total_height = DEFAULT_BACKSCROLL;
1593
    s->x = 0;
1594
    s->y = 0;
1595
    if (!s->surface) {
1596
        if (active_console && active_console->surface) {
1597
            g_width = surface_width(active_console->surface);
1598
            g_height = surface_height(active_console->surface);
1599
        }
1600
        s->surface = qemu_create_displaysurface(g_width, g_height);
1601
    }
1602

    
1603
    s->cursor_timer =
1604
        qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
1605

    
1606
    s->hw_ops = &text_console_ops;
1607
    s->hw = s;
1608

    
1609
    /* Set text attribute defaults */
1610
    s->t_attrib_default.bold = 0;
1611
    s->t_attrib_default.uline = 0;
1612
    s->t_attrib_default.blink = 0;
1613
    s->t_attrib_default.invers = 0;
1614
    s->t_attrib_default.unvisible = 0;
1615
    s->t_attrib_default.fgcol = COLOR_WHITE;
1616
    s->t_attrib_default.bgcol = COLOR_BLACK;
1617
    /* set current text attributes to default */
1618
    s->t_attrib = s->t_attrib_default;
1619
    text_console_resize(s);
1620

    
1621
    if (chr->label) {
1622
        char msg[128];
1623
        int len;
1624

    
1625
        s->t_attrib.bgcol = COLOR_BLUE;
1626
        len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1627
        console_puts(chr, (uint8_t*)msg, len);
1628
        s->t_attrib = s->t_attrib_default;
1629
    }
1630

    
1631
    qemu_chr_be_generic_open(chr);
1632
    if (chr->init)
1633
        chr->init(chr);
1634
}
1635

    
1636
static CharDriverState *text_console_init(ChardevVC *vc)
1637
{
1638
    CharDriverState *chr;
1639
    QemuConsole *s;
1640
    unsigned width = 0;
1641
    unsigned height = 0;
1642

    
1643
    chr = g_malloc0(sizeof(CharDriverState));
1644

    
1645
    if (vc->has_width) {
1646
        width = vc->width;
1647
    } else if (vc->has_cols) {
1648
        width = vc->cols * FONT_WIDTH;
1649
    }
1650

    
1651
    if (vc->has_height) {
1652
        height = vc->height;
1653
    } else if (vc->has_rows) {
1654
        height = vc->rows * FONT_HEIGHT;
1655
    }
1656

    
1657
    trace_console_txt_new(width, height);
1658
    if (width == 0 || height == 0) {
1659
        s = new_console(NULL, TEXT_CONSOLE);
1660
    } else {
1661
        s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1662
        s->surface = qemu_create_displaysurface(width, height);
1663
    }
1664

    
1665
    if (!s) {
1666
        g_free(chr);
1667
        return NULL;
1668
    }
1669

    
1670
    s->chr = chr;
1671
    chr->opaque = s;
1672
    chr->chr_set_echo = text_console_set_echo;
1673

    
1674
    if (display_state) {
1675
        text_console_do_init(chr, display_state);
1676
    }
1677
    return chr;
1678
}
1679

    
1680
static VcHandler *vc_handler = text_console_init;
1681

    
1682
CharDriverState *vc_init(ChardevVC *vc)
1683
{
1684
    return vc_handler(vc);
1685
}
1686

    
1687
void register_vc_handler(VcHandler *handler)
1688
{
1689
    vc_handler = handler;
1690
}
1691

    
1692
void qemu_console_resize(QemuConsole *s, int width, int height)
1693
{
1694
    DisplaySurface *surface;
1695

    
1696
    assert(s->console_type == GRAPHIC_CONSOLE);
1697
    surface = qemu_create_displaysurface(width, height);
1698
    dpy_gfx_replace_surface(s, surface);
1699
}
1700

    
1701
void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
1702
                       int dst_x, int dst_y, int w, int h)
1703
{
1704
    assert(con->console_type == GRAPHIC_CONSOLE);
1705
    dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
1706
}
1707

    
1708
DisplaySurface *qemu_console_surface(QemuConsole *console)
1709
{
1710
    return console->surface;
1711
}
1712

    
1713
DisplayState *qemu_console_displaystate(QemuConsole *console)
1714
{
1715
    return console->ds;
1716
}
1717

    
1718
PixelFormat qemu_different_endianness_pixelformat(int bpp)
1719
{
1720
    PixelFormat pf;
1721

    
1722
    memset(&pf, 0x00, sizeof(PixelFormat));
1723

    
1724
    pf.bits_per_pixel = bpp;
1725
    pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1726
    pf.depth = bpp == 32 ? 24 : bpp;
1727

    
1728
    switch (bpp) {
1729
        case 24:
1730
            pf.rmask = 0x000000FF;
1731
            pf.gmask = 0x0000FF00;
1732
            pf.bmask = 0x00FF0000;
1733
            pf.rmax = 255;
1734
            pf.gmax = 255;
1735
            pf.bmax = 255;
1736
            pf.rshift = 0;
1737
            pf.gshift = 8;
1738
            pf.bshift = 16;
1739
            pf.rbits = 8;
1740
            pf.gbits = 8;
1741
            pf.bbits = 8;
1742
            break;
1743
        case 32:
1744
            pf.rmask = 0x0000FF00;
1745
            pf.gmask = 0x00FF0000;
1746
            pf.bmask = 0xFF000000;
1747
            pf.amask = 0x00000000;
1748
            pf.amax = 255;
1749
            pf.rmax = 255;
1750
            pf.gmax = 255;
1751
            pf.bmax = 255;
1752
            pf.ashift = 0;
1753
            pf.rshift = 8;
1754
            pf.gshift = 16;
1755
            pf.bshift = 24;
1756
            pf.rbits = 8;
1757
            pf.gbits = 8;
1758
            pf.bbits = 8;
1759
            pf.abits = 8;
1760
            break;
1761
        default:
1762
            break;
1763
    }
1764
    return pf;
1765
}
1766

    
1767
PixelFormat qemu_default_pixelformat(int bpp)
1768
{
1769
    PixelFormat pf;
1770

    
1771
    memset(&pf, 0x00, sizeof(PixelFormat));
1772

    
1773
    pf.bits_per_pixel = bpp;
1774
    pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1775
    pf.depth = bpp == 32 ? 24 : bpp;
1776

    
1777
    switch (bpp) {
1778
        case 15:
1779
            pf.bits_per_pixel = 16;
1780
            pf.rmask = 0x00007c00;
1781
            pf.gmask = 0x000003E0;
1782
            pf.bmask = 0x0000001F;
1783
            pf.rmax = 31;
1784
            pf.gmax = 31;
1785
            pf.bmax = 31;
1786
            pf.rshift = 10;
1787
            pf.gshift = 5;
1788
            pf.bshift = 0;
1789
            pf.rbits = 5;
1790
            pf.gbits = 5;
1791
            pf.bbits = 5;
1792
            break;
1793
        case 16:
1794
            pf.rmask = 0x0000F800;
1795
            pf.gmask = 0x000007E0;
1796
            pf.bmask = 0x0000001F;
1797
            pf.rmax = 31;
1798
            pf.gmax = 63;
1799
            pf.bmax = 31;
1800
            pf.rshift = 11;
1801
            pf.gshift = 5;
1802
            pf.bshift = 0;
1803
            pf.rbits = 5;
1804
            pf.gbits = 6;
1805
            pf.bbits = 5;
1806
            break;
1807
        case 24:
1808
            pf.rmask = 0x00FF0000;
1809
            pf.gmask = 0x0000FF00;
1810
            pf.bmask = 0x000000FF;
1811
            pf.rmax = 255;
1812
            pf.gmax = 255;
1813
            pf.bmax = 255;
1814
            pf.rshift = 16;
1815
            pf.gshift = 8;
1816
            pf.bshift = 0;
1817
            pf.rbits = 8;
1818
            pf.gbits = 8;
1819
            pf.bbits = 8;
1820
            break;
1821
        case 32:
1822
            pf.rmask = 0x00FF0000;
1823
            pf.gmask = 0x0000FF00;
1824
            pf.bmask = 0x000000FF;
1825
            pf.rmax = 255;
1826
            pf.gmax = 255;
1827
            pf.bmax = 255;
1828
            pf.rshift = 16;
1829
            pf.gshift = 8;
1830
            pf.bshift = 0;
1831
            pf.rbits = 8;
1832
            pf.gbits = 8;
1833
            pf.bbits = 8;
1834
            break;
1835
        default:
1836
            break;
1837
    }
1838
    return pf;
1839
}
1840

    
1841
static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
1842
                              Error **errp)
1843
{
1844
    int val;
1845

    
1846
    backend->vc = g_new0(ChardevVC, 1);
1847

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

    
1854
    val = qemu_opt_get_number(opts, "height", 0);
1855
    if (val != 0) {
1856
        backend->vc->has_height = true;
1857
        backend->vc->height = val;
1858
    }
1859

    
1860
    val = qemu_opt_get_number(opts, "cols", 0);
1861
    if (val != 0) {
1862
        backend->vc->has_cols = true;
1863
        backend->vc->cols = val;
1864
    }
1865

    
1866
    val = qemu_opt_get_number(opts, "rows", 0);
1867
    if (val != 0) {
1868
        backend->vc->has_rows = true;
1869
        backend->vc->rows = val;
1870
    }
1871
}
1872

    
1873
static void register_types(void)
1874
{
1875
    register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
1876
                              qemu_chr_parse_vc);
1877
}
1878

    
1879
type_init(register_types);