Statistics
| Branch: | Revision:

root / ui / console.c @ 95be0669

History | View | Annotate | Download (51.8 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
    Object parent;
117

    
118
    int index;
119
    console_type_t console_type;
120
    DisplayState *ds;
121
    DisplaySurface *surface;
122
    int dcls;
123

    
124
    /* Graphic console state.  */
125
    const GraphicHwOps *hw_ops;
126
    void *hw;
127

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

    
145
    int update_x0;
146
    int update_y0;
147
    int update_x1;
148
    int update_y1;
149

    
150
    enum TTYState state;
151
    int esc_params[MAX_ESC_PARAMS];
152
    int nb_esc_params;
153

    
154
    CharDriverState *chr;
155
    /* fifo for key pressed */
156
    QEMUFIFO out_fifo;
157
    uint8_t out_fifo_buf[16];
158
    QEMUTimer *kbd_timer;
159
};
160

    
161
struct DisplayState {
162
    struct QEMUTimer *gui_timer;
163
    uint64_t last_update;
164
    uint64_t update_interval;
165
    bool refreshing;
166
    bool have_gfx;
167
    bool have_text;
168

    
169
    QLIST_HEAD(, DisplayChangeListener) listeners;
170
};
171

    
172
static DisplayState *display_state;
173
static QemuConsole *active_console;
174
static QemuConsole *consoles[MAX_CONSOLES];
175
static int nb_consoles = 0;
176

    
177
static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
178
static void dpy_refresh(DisplayState *s);
179

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

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

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

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

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

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

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

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

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

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

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

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

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

    
311
void qmp_screendump(const char *filename, Error **errp)
312
{
313
    QemuConsole *con = qemu_console_lookup_by_index(0);
314
    DisplaySurface *surface;
315

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

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

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

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

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

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

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

    
360
/***********************************************************/
361
/* basic char display */
362

    
363
#define FONT_HEIGHT 16
364
#define FONT_WIDTH 8
365

    
366
#include "vgafont.h"
367

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
519
    if (!qemu_console_is_visible(s)) {
520
        return;
521
    }
522

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

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

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

    
547
    if (!qemu_console_is_visible(s)) {
548
        return;
549
    }
550

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

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

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

    
583
    if (!qemu_console_is_visible(s)) {
584
        return;
585
    }
586

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

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

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

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

    
645
static void console_put_lf(QemuConsole *s)
646
{
647
    TextCell *c;
648
    int x, y1;
649

    
650
    s->y++;
651
    if (s->y >= s->height) {
652
        s->y = s->height - 1;
653

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

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

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

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

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

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

    
815
    s->x = x;
816
    s->y = y;
817
}
818

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

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

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

    
1024
void console_select(unsigned int index)
1025
{
1026
    DisplayChangeListener *dcl;
1027
    QemuConsole *s;
1028

    
1029
    if (index >= MAX_CONSOLES)
1030
        return;
1031

    
1032
    trace_console_select(index);
1033
    s = qemu_console_lookup_by_index(index);
1034
    if (s) {
1035
        DisplayState *ds = s->ds;
1036

    
1037
        if (active_console && active_console->cursor_timer) {
1038
            qemu_del_timer(active_console->cursor_timer);
1039
        }
1040
        active_console = s;
1041
        if (ds->have_gfx) {
1042
            QLIST_FOREACH(dcl, &ds->listeners, next) {
1043
                if (dcl->con != NULL) {
1044
                    continue;
1045
                }
1046
                if (dcl->ops->dpy_gfx_switch) {
1047
                    dcl->ops->dpy_gfx_switch(dcl, s->surface);
1048
                }
1049
            }
1050
            dpy_gfx_update(s, 0, 0, surface_width(s->surface),
1051
                           surface_height(s->surface));
1052
        }
1053
        if (ds->have_text) {
1054
            dpy_text_resize(s, s->width, s->height);
1055
        }
1056
        if (s->cursor_timer) {
1057
            qemu_mod_timer(s->cursor_timer,
1058
                   qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1059
        }
1060
    }
1061
}
1062

    
1063
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1064
{
1065
    QemuConsole *s = chr->opaque;
1066
    int i;
1067

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

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

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

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

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

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

    
1163
static void text_console_invalidate(void *opaque)
1164
{
1165
    QemuConsole *s = (QemuConsole *) opaque;
1166

    
1167
    if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1168
        text_console_resize(s);
1169
    }
1170
    console_refresh(s);
1171
}
1172

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

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

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

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

    
1234
static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1235
                               int linesize, PixelFormat pf, int newflags)
1236
{
1237
    surface->pf = pf;
1238

    
1239
    qemu_pixman_image_unref(surface->image);
1240
    surface->image = NULL;
1241

    
1242
    surface->format = qemu_pixman_get_format(&pf);
1243
    assert(surface->format != 0);
1244
    surface->image = pixman_image_create_bits(surface->format,
1245
                                              width, height,
1246
                                              NULL, linesize);
1247
    assert(surface->image != NULL);
1248

    
1249
    surface->flags = newflags | QEMU_ALLOCATED_FLAG;
1250
#ifdef HOST_WORDS_BIGENDIAN
1251
    surface->flags |= QEMU_BIG_ENDIAN_FLAG;
1252
#endif
1253
}
1254

    
1255
DisplaySurface *qemu_create_displaysurface(int width, int height)
1256
{
1257
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1258
    int linesize = width * 4;
1259

    
1260
    trace_displaysurface_create(surface, width, height);
1261
    qemu_alloc_display(surface, width, height, linesize,
1262
                       qemu_default_pixelformat(32), 0);
1263
    return surface;
1264
}
1265

    
1266
DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
1267
                                                int linesize, uint8_t *data,
1268
                                                bool byteswap)
1269
{
1270
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1271

    
1272
    trace_displaysurface_create_from(surface, width, height, bpp, byteswap);
1273
    if (byteswap) {
1274
        surface->pf = qemu_different_endianness_pixelformat(bpp);
1275
    } else {
1276
        surface->pf = qemu_default_pixelformat(bpp);
1277
    }
1278

    
1279
    surface->format = qemu_pixman_get_format(&surface->pf);
1280
    assert(surface->format != 0);
1281
    surface->image = pixman_image_create_bits(surface->format,
1282
                                              width, height,
1283
                                              (void *)data, linesize);
1284
    assert(surface->image != NULL);
1285

    
1286
#ifdef HOST_WORDS_BIGENDIAN
1287
    surface->flags = QEMU_BIG_ENDIAN_FLAG;
1288
#endif
1289

    
1290
    return surface;
1291
}
1292

    
1293
void qemu_free_displaysurface(DisplaySurface *surface)
1294
{
1295
    if (surface == NULL) {
1296
        return;
1297
    }
1298
    trace_displaysurface_free(surface);
1299
    qemu_pixman_image_unref(surface->image);
1300
    g_free(surface);
1301
}
1302

    
1303
void register_displaychangelistener(DisplayState *ds,
1304
                                    DisplayChangeListener *dcl)
1305
{
1306
    QemuConsole *con;
1307

    
1308
    trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1309
    dcl->ds = ds;
1310
    QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
1311
    gui_setup_refresh(ds);
1312
    if (dcl->con) {
1313
        dcl->con->dcls++;
1314
        con = dcl->con;
1315
    } else {
1316
        con = active_console;
1317
    }
1318
    if (dcl->ops->dpy_gfx_switch && con) {
1319
        dcl->ops->dpy_gfx_switch(dcl, con->surface);
1320
    }
1321
}
1322

    
1323
void update_displaychangelistener(DisplayChangeListener *dcl,
1324
                                  uint64_t interval)
1325
{
1326
    DisplayState *ds = dcl->ds;
1327

    
1328
    dcl->update_interval = interval;
1329
    if (!ds->refreshing && ds->update_interval > interval) {
1330
        qemu_mod_timer(ds->gui_timer, ds->last_update + interval);
1331
    }
1332
}
1333

    
1334
void unregister_displaychangelistener(DisplayChangeListener *dcl)
1335
{
1336
    DisplayState *ds = dcl->ds;
1337
    trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1338
    if (dcl->con) {
1339
        dcl->con->dcls--;
1340
    }
1341
    QLIST_REMOVE(dcl, next);
1342
    gui_setup_refresh(ds);
1343
}
1344

    
1345
void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1346
{
1347
    DisplayState *s = con->ds;
1348
    DisplayChangeListener *dcl;
1349
    int width = surface_width(con->surface);
1350
    int height = surface_height(con->surface);
1351

    
1352
    x = MAX(x, 0);
1353
    y = MAX(y, 0);
1354
    x = MIN(x, width);
1355
    y = MIN(y, height);
1356
    w = MIN(w, width - x);
1357
    h = MIN(h, height - y);
1358

    
1359
    if (!qemu_console_is_visible(con)) {
1360
        return;
1361
    }
1362
    QLIST_FOREACH(dcl, &s->listeners, next) {
1363
        if (con != (dcl->con ? dcl->con : active_console)) {
1364
            continue;
1365
        }
1366
        if (dcl->ops->dpy_gfx_update) {
1367
            dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1368
        }
1369
    }
1370
}
1371

    
1372
void dpy_gfx_replace_surface(QemuConsole *con,
1373
                             DisplaySurface *surface)
1374
{
1375
    DisplayState *s = con->ds;
1376
    DisplaySurface *old_surface = con->surface;
1377
    DisplayChangeListener *dcl;
1378

    
1379
    con->surface = surface;
1380
    QLIST_FOREACH(dcl, &s->listeners, next) {
1381
        if (con != (dcl->con ? dcl->con : active_console)) {
1382
            continue;
1383
        }
1384
        if (dcl->ops->dpy_gfx_switch) {
1385
            dcl->ops->dpy_gfx_switch(dcl, surface);
1386
        }
1387
    }
1388
    qemu_free_displaysurface(old_surface);
1389
}
1390

    
1391
void dpy_refresh(DisplayState *s)
1392
{
1393
    DisplayChangeListener *dcl;
1394

    
1395
    QLIST_FOREACH(dcl, &s->listeners, next) {
1396
        if (dcl->ops->dpy_refresh) {
1397
            dcl->ops->dpy_refresh(dcl);
1398
        }
1399
    }
1400
}
1401

    
1402
void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
1403
                  int dst_x, int dst_y, int w, int h)
1404
{
1405
    DisplayState *s = con->ds;
1406
    DisplayChangeListener *dcl;
1407

    
1408
    if (!qemu_console_is_visible(con)) {
1409
        return;
1410
    }
1411
    QLIST_FOREACH(dcl, &s->listeners, next) {
1412
        if (con != (dcl->con ? dcl->con : active_console)) {
1413
            continue;
1414
        }
1415
        if (dcl->ops->dpy_gfx_copy) {
1416
            dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
1417
        } else { /* TODO */
1418
            dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
1419
        }
1420
    }
1421
}
1422

    
1423
void dpy_text_cursor(QemuConsole *con, int x, int y)
1424
{
1425
    DisplayState *s = con->ds;
1426
    DisplayChangeListener *dcl;
1427

    
1428
    if (!qemu_console_is_visible(con)) {
1429
        return;
1430
    }
1431
    QLIST_FOREACH(dcl, &s->listeners, next) {
1432
        if (con != (dcl->con ? dcl->con : active_console)) {
1433
            continue;
1434
        }
1435
        if (dcl->ops->dpy_text_cursor) {
1436
            dcl->ops->dpy_text_cursor(dcl, x, y);
1437
        }
1438
    }
1439
}
1440

    
1441
void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1442
{
1443
    DisplayState *s = con->ds;
1444
    DisplayChangeListener *dcl;
1445

    
1446
    if (!qemu_console_is_visible(con)) {
1447
        return;
1448
    }
1449
    QLIST_FOREACH(dcl, &s->listeners, next) {
1450
        if (con != (dcl->con ? dcl->con : active_console)) {
1451
            continue;
1452
        }
1453
        if (dcl->ops->dpy_text_update) {
1454
            dcl->ops->dpy_text_update(dcl, x, y, w, h);
1455
        }
1456
    }
1457
}
1458

    
1459
void dpy_text_resize(QemuConsole *con, int w, int h)
1460
{
1461
    DisplayState *s = con->ds;
1462
    struct DisplayChangeListener *dcl;
1463

    
1464
    if (!qemu_console_is_visible(con)) {
1465
        return;
1466
    }
1467
    QLIST_FOREACH(dcl, &s->listeners, next) {
1468
        if (con != (dcl->con ? dcl->con : active_console)) {
1469
            continue;
1470
        }
1471
        if (dcl->ops->dpy_text_resize) {
1472
            dcl->ops->dpy_text_resize(dcl, w, h);
1473
        }
1474
    }
1475
}
1476

    
1477
void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1478
{
1479
    DisplayState *s = con->ds;
1480
    DisplayChangeListener *dcl;
1481

    
1482
    if (!qemu_console_is_visible(con)) {
1483
        return;
1484
    }
1485
    QLIST_FOREACH(dcl, &s->listeners, next) {
1486
        if (con != (dcl->con ? dcl->con : active_console)) {
1487
            continue;
1488
        }
1489
        if (dcl->ops->dpy_mouse_set) {
1490
            dcl->ops->dpy_mouse_set(dcl, x, y, on);
1491
        }
1492
    }
1493
}
1494

    
1495
void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1496
{
1497
    DisplayState *s = con->ds;
1498
    DisplayChangeListener *dcl;
1499

    
1500
    if (!qemu_console_is_visible(con)) {
1501
        return;
1502
    }
1503
    QLIST_FOREACH(dcl, &s->listeners, next) {
1504
        if (con != (dcl->con ? dcl->con : active_console)) {
1505
            continue;
1506
        }
1507
        if (dcl->ops->dpy_cursor_define) {
1508
            dcl->ops->dpy_cursor_define(dcl, cursor);
1509
        }
1510
    }
1511
}
1512

    
1513
bool dpy_cursor_define_supported(QemuConsole *con)
1514
{
1515
    DisplayState *s = con->ds;
1516
    DisplayChangeListener *dcl;
1517

    
1518
    QLIST_FOREACH(dcl, &s->listeners, next) {
1519
        if (dcl->ops->dpy_cursor_define) {
1520
            return true;
1521
        }
1522
    }
1523
    return false;
1524
}
1525

    
1526
/***********************************************************/
1527
/* register display */
1528

    
1529
/* console.c internal use only */
1530
static DisplayState *get_alloc_displaystate(void)
1531
{
1532
    if (!display_state) {
1533
        display_state = g_new0(DisplayState, 1);
1534
    }
1535
    return display_state;
1536
}
1537

    
1538
/*
1539
 * Called by main(), after creating QemuConsoles
1540
 * and before initializing ui (sdl/vnc/...).
1541
 */
1542
DisplayState *init_displaystate(void)
1543
{
1544
    int i;
1545

    
1546
    if (!display_state) {
1547
        display_state = g_new0(DisplayState, 1);
1548
    }
1549

    
1550
    for (i = 0; i < nb_consoles; i++) {
1551
        if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
1552
            consoles[i]->ds == NULL) {
1553
            text_console_do_init(consoles[i]->chr, display_state);
1554
        }
1555
    }
1556

    
1557
    return display_state;
1558
}
1559

    
1560
QemuConsole *graphic_console_init(const GraphicHwOps *hw_ops,
1561
                                  void *opaque)
1562
{
1563
    int width = 640;
1564
    int height = 480;
1565
    QemuConsole *s;
1566
    DisplayState *ds;
1567

    
1568
    ds = get_alloc_displaystate();
1569
    trace_console_gfx_new();
1570
    s = new_console(ds, GRAPHIC_CONSOLE);
1571
    s->hw_ops = hw_ops;
1572
    s->hw = opaque;
1573

    
1574
    s->surface = qemu_create_displaysurface(width, height);
1575
    return s;
1576
}
1577

    
1578
QemuConsole *qemu_console_lookup_by_index(unsigned int index)
1579
{
1580
    if (index >= MAX_CONSOLES) {
1581
        return NULL;
1582
    }
1583
    return consoles[index];
1584
}
1585

    
1586
bool qemu_console_is_visible(QemuConsole *con)
1587
{
1588
    return (con == active_console) || (con->dcls > 0);
1589
}
1590

    
1591
bool qemu_console_is_graphic(QemuConsole *con)
1592
{
1593
    if (con == NULL) {
1594
        con = active_console;
1595
    }
1596
    return con && (con->console_type == GRAPHIC_CONSOLE);
1597
}
1598

    
1599
bool qemu_console_is_fixedsize(QemuConsole *con)
1600
{
1601
    if (con == NULL) {
1602
        con = active_console;
1603
    }
1604
    return con && (con->console_type != TEXT_CONSOLE);
1605
}
1606

    
1607
static void text_console_set_echo(CharDriverState *chr, bool echo)
1608
{
1609
    QemuConsole *s = chr->opaque;
1610

    
1611
    s->echo = echo;
1612
}
1613

    
1614
static void text_console_update_cursor(void *opaque)
1615
{
1616
    QemuConsole *s = opaque;
1617

    
1618
    s->cursor_visible_phase = !s->cursor_visible_phase;
1619
    graphic_hw_invalidate(s);
1620
    qemu_mod_timer(s->cursor_timer,
1621
                   qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
1622
}
1623

    
1624
static const GraphicHwOps text_console_ops = {
1625
    .invalidate  = text_console_invalidate,
1626
    .text_update = text_console_update,
1627
};
1628

    
1629
static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
1630
{
1631
    QemuConsole *s;
1632
    int g_width = 80 * FONT_WIDTH;
1633
    int g_height = 24 * FONT_HEIGHT;
1634

    
1635
    s = chr->opaque;
1636

    
1637
    chr->chr_write = console_puts;
1638

    
1639
    s->out_fifo.buf = s->out_fifo_buf;
1640
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1641
    s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
1642
    s->ds = ds;
1643

    
1644
    s->y_displayed = 0;
1645
    s->y_base = 0;
1646
    s->total_height = DEFAULT_BACKSCROLL;
1647
    s->x = 0;
1648
    s->y = 0;
1649
    if (!s->surface) {
1650
        if (active_console && active_console->surface) {
1651
            g_width = surface_width(active_console->surface);
1652
            g_height = surface_height(active_console->surface);
1653
        }
1654
        s->surface = qemu_create_displaysurface(g_width, g_height);
1655
    }
1656

    
1657
    s->cursor_timer =
1658
        qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
1659

    
1660
    s->hw_ops = &text_console_ops;
1661
    s->hw = s;
1662

    
1663
    /* Set text attribute defaults */
1664
    s->t_attrib_default.bold = 0;
1665
    s->t_attrib_default.uline = 0;
1666
    s->t_attrib_default.blink = 0;
1667
    s->t_attrib_default.invers = 0;
1668
    s->t_attrib_default.unvisible = 0;
1669
    s->t_attrib_default.fgcol = COLOR_WHITE;
1670
    s->t_attrib_default.bgcol = COLOR_BLACK;
1671
    /* set current text attributes to default */
1672
    s->t_attrib = s->t_attrib_default;
1673
    text_console_resize(s);
1674

    
1675
    if (chr->label) {
1676
        char msg[128];
1677
        int len;
1678

    
1679
        s->t_attrib.bgcol = COLOR_BLUE;
1680
        len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1681
        console_puts(chr, (uint8_t*)msg, len);
1682
        s->t_attrib = s->t_attrib_default;
1683
    }
1684

    
1685
    qemu_chr_be_generic_open(chr);
1686
    if (chr->init)
1687
        chr->init(chr);
1688
}
1689

    
1690
static CharDriverState *text_console_init(ChardevVC *vc)
1691
{
1692
    CharDriverState *chr;
1693
    QemuConsole *s;
1694
    unsigned width = 0;
1695
    unsigned height = 0;
1696

    
1697
    chr = g_malloc0(sizeof(CharDriverState));
1698

    
1699
    if (vc->has_width) {
1700
        width = vc->width;
1701
    } else if (vc->has_cols) {
1702
        width = vc->cols * FONT_WIDTH;
1703
    }
1704

    
1705
    if (vc->has_height) {
1706
        height = vc->height;
1707
    } else if (vc->has_rows) {
1708
        height = vc->rows * FONT_HEIGHT;
1709
    }
1710

    
1711
    trace_console_txt_new(width, height);
1712
    if (width == 0 || height == 0) {
1713
        s = new_console(NULL, TEXT_CONSOLE);
1714
    } else {
1715
        s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
1716
        s->surface = qemu_create_displaysurface(width, height);
1717
    }
1718

    
1719
    if (!s) {
1720
        g_free(chr);
1721
        return NULL;
1722
    }
1723

    
1724
    s->chr = chr;
1725
    chr->opaque = s;
1726
    chr->chr_set_echo = text_console_set_echo;
1727

    
1728
    if (display_state) {
1729
        text_console_do_init(chr, display_state);
1730
    }
1731
    return chr;
1732
}
1733

    
1734
static VcHandler *vc_handler = text_console_init;
1735

    
1736
CharDriverState *vc_init(ChardevVC *vc)
1737
{
1738
    return vc_handler(vc);
1739
}
1740

    
1741
void register_vc_handler(VcHandler *handler)
1742
{
1743
    vc_handler = handler;
1744
}
1745

    
1746
void qemu_console_resize(QemuConsole *s, int width, int height)
1747
{
1748
    DisplaySurface *surface;
1749

    
1750
    assert(s->console_type == GRAPHIC_CONSOLE);
1751
    surface = qemu_create_displaysurface(width, height);
1752
    dpy_gfx_replace_surface(s, surface);
1753
}
1754

    
1755
void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
1756
                       int dst_x, int dst_y, int w, int h)
1757
{
1758
    assert(con->console_type == GRAPHIC_CONSOLE);
1759
    dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
1760
}
1761

    
1762
DisplaySurface *qemu_console_surface(QemuConsole *console)
1763
{
1764
    return console->surface;
1765
}
1766

    
1767
DisplayState *qemu_console_displaystate(QemuConsole *console)
1768
{
1769
    return console->ds;
1770
}
1771

    
1772
PixelFormat qemu_different_endianness_pixelformat(int bpp)
1773
{
1774
    PixelFormat pf;
1775

    
1776
    memset(&pf, 0x00, sizeof(PixelFormat));
1777

    
1778
    pf.bits_per_pixel = bpp;
1779
    pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1780
    pf.depth = bpp == 32 ? 24 : bpp;
1781

    
1782
    switch (bpp) {
1783
        case 24:
1784
            pf.rmask = 0x000000FF;
1785
            pf.gmask = 0x0000FF00;
1786
            pf.bmask = 0x00FF0000;
1787
            pf.rmax = 255;
1788
            pf.gmax = 255;
1789
            pf.bmax = 255;
1790
            pf.rshift = 0;
1791
            pf.gshift = 8;
1792
            pf.bshift = 16;
1793
            pf.rbits = 8;
1794
            pf.gbits = 8;
1795
            pf.bbits = 8;
1796
            break;
1797
        case 32:
1798
            pf.rmask = 0x0000FF00;
1799
            pf.gmask = 0x00FF0000;
1800
            pf.bmask = 0xFF000000;
1801
            pf.amask = 0x00000000;
1802
            pf.amax = 255;
1803
            pf.rmax = 255;
1804
            pf.gmax = 255;
1805
            pf.bmax = 255;
1806
            pf.ashift = 0;
1807
            pf.rshift = 8;
1808
            pf.gshift = 16;
1809
            pf.bshift = 24;
1810
            pf.rbits = 8;
1811
            pf.gbits = 8;
1812
            pf.bbits = 8;
1813
            pf.abits = 8;
1814
            break;
1815
        default:
1816
            break;
1817
    }
1818
    return pf;
1819
}
1820

    
1821
PixelFormat qemu_default_pixelformat(int bpp)
1822
{
1823
    PixelFormat pf;
1824

    
1825
    memset(&pf, 0x00, sizeof(PixelFormat));
1826

    
1827
    pf.bits_per_pixel = bpp;
1828
    pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
1829
    pf.depth = bpp == 32 ? 24 : bpp;
1830

    
1831
    switch (bpp) {
1832
        case 15:
1833
            pf.bits_per_pixel = 16;
1834
            pf.rmask = 0x00007c00;
1835
            pf.gmask = 0x000003E0;
1836
            pf.bmask = 0x0000001F;
1837
            pf.rmax = 31;
1838
            pf.gmax = 31;
1839
            pf.bmax = 31;
1840
            pf.rshift = 10;
1841
            pf.gshift = 5;
1842
            pf.bshift = 0;
1843
            pf.rbits = 5;
1844
            pf.gbits = 5;
1845
            pf.bbits = 5;
1846
            break;
1847
        case 16:
1848
            pf.rmask = 0x0000F800;
1849
            pf.gmask = 0x000007E0;
1850
            pf.bmask = 0x0000001F;
1851
            pf.rmax = 31;
1852
            pf.gmax = 63;
1853
            pf.bmax = 31;
1854
            pf.rshift = 11;
1855
            pf.gshift = 5;
1856
            pf.bshift = 0;
1857
            pf.rbits = 5;
1858
            pf.gbits = 6;
1859
            pf.bbits = 5;
1860
            break;
1861
        case 24:
1862
            pf.rmask = 0x00FF0000;
1863
            pf.gmask = 0x0000FF00;
1864
            pf.bmask = 0x000000FF;
1865
            pf.rmax = 255;
1866
            pf.gmax = 255;
1867
            pf.bmax = 255;
1868
            pf.rshift = 16;
1869
            pf.gshift = 8;
1870
            pf.bshift = 0;
1871
            pf.rbits = 8;
1872
            pf.gbits = 8;
1873
            pf.bbits = 8;
1874
            break;
1875
        case 32:
1876
            pf.rmask = 0x00FF0000;
1877
            pf.gmask = 0x0000FF00;
1878
            pf.bmask = 0x000000FF;
1879
            pf.rmax = 255;
1880
            pf.gmax = 255;
1881
            pf.bmax = 255;
1882
            pf.rshift = 16;
1883
            pf.gshift = 8;
1884
            pf.bshift = 0;
1885
            pf.rbits = 8;
1886
            pf.gbits = 8;
1887
            pf.bbits = 8;
1888
            break;
1889
        default:
1890
            break;
1891
    }
1892
    return pf;
1893
}
1894

    
1895
static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
1896
                              Error **errp)
1897
{
1898
    int val;
1899

    
1900
    backend->vc = g_new0(ChardevVC, 1);
1901

    
1902
    val = qemu_opt_get_number(opts, "width", 0);
1903
    if (val != 0) {
1904
        backend->vc->has_width = true;
1905
        backend->vc->width = val;
1906
    }
1907

    
1908
    val = qemu_opt_get_number(opts, "height", 0);
1909
    if (val != 0) {
1910
        backend->vc->has_height = true;
1911
        backend->vc->height = val;
1912
    }
1913

    
1914
    val = qemu_opt_get_number(opts, "cols", 0);
1915
    if (val != 0) {
1916
        backend->vc->has_cols = true;
1917
        backend->vc->cols = val;
1918
    }
1919

    
1920
    val = qemu_opt_get_number(opts, "rows", 0);
1921
    if (val != 0) {
1922
        backend->vc->has_rows = true;
1923
        backend->vc->rows = val;
1924
    }
1925
}
1926

    
1927
static const TypeInfo qemu_console_info = {
1928
    .name = TYPE_QEMU_CONSOLE,
1929
    .parent = TYPE_OBJECT,
1930
    .instance_size = sizeof(QemuConsole),
1931
    .class_size = sizeof(QemuConsoleClass),
1932
};
1933

    
1934

    
1935
static void register_types(void)
1936
{
1937
    type_register_static(&qemu_console_info);
1938
    register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
1939
                              qemu_chr_parse_vc);
1940
}
1941

    
1942
type_init(register_types);