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

P
Peter Maydell 已提交
25
#include "qemu/osdep.h"
26
#include "ui/console.h"
27
#include "hw/qdev-core.h"
28
#include "qapi/error.h"
29
#include "qapi/qapi-commands-ui.h"
30
#include "qemu/option.h"
31
#include "qemu/timer.h"
32
#include "chardev/char-fe.h"
33
#include "trace.h"
34
#include "exec/memory.h"
B
bellard 已提交
35 36

#define DEFAULT_BACKSCROLL 512
37
#define CONSOLE_CURSOR_PERIOD 500
B
bellard 已提交
38

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

B
bellard 已提交
49 50
typedef struct TextCell {
    uint8_t ch;
51
    TextAttributes t_attrib;
B
bellard 已提交
52 53 54 55 56 57 58 59 60 61
} TextCell;

#define MAX_ESC_PARAMS 3

enum TTYState {
    TTY_STATE_NORM,
    TTY_STATE_ESC,
    TTY_STATE_CSI,
};

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

68
static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
{
    int l, len;

    l = f->buf_size - f->count;
    if (len1 > l)
        len1 = l;
    len = len1;
    while (len > 0) {
        l = f->buf_size - f->wptr;
        if (l > len)
            l = len;
        memcpy(f->buf + f->wptr, buf, l);
        f->wptr += l;
        if (f->wptr >= f->buf_size)
            f->wptr = 0;
        buf += l;
        len -= l;
    }
    f->count += len1;
    return len1;
}

91
static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
{
    int l, len;

    if (len1 > f->count)
        len1 = f->count;
    len = len1;
    while (len > 0) {
        l = f->buf_size - f->rptr;
        if (l > len)
            l = len;
        memcpy(buf, f->buf + f->rptr, l);
        f->rptr += l;
        if (f->rptr >= f->buf_size)
            f->rptr = 0;
        buf += l;
        len -= l;
    }
    f->count -= len1;
    return len1;
}

113 114
typedef enum {
    GRAPHIC_CONSOLE,
115 116
    TEXT_CONSOLE,
    TEXT_CONSOLE_FIXED_SIZE
A
Anthony Liguori 已提交
117
} console_type_t;
118

119
struct QemuConsole {
G
Gerd Hoffmann 已提交
120 121
    Object parent;

122
    int index;
A
Anthony Liguori 已提交
123
    console_type_t console_type;
B
bellard 已提交
124
    DisplayState *ds;
125
    DisplaySurface *surface;
126
    int dcls;
127
    DisplayChangeListener *gl;
128
    bool gl_block;
129
    int window_id;
130

P
pbrook 已提交
131
    /* Graphic console state.  */
132
    Object *device;
133
    uint32_t head;
G
Gerd Hoffmann 已提交
134
    QemuUIInfo ui_info;
135
    QEMUTimer *ui_timer;
G
Gerd Hoffmann 已提交
136
    const GraphicHwOps *hw_ops;
P
pbrook 已提交
137
    void *hw;
138 139

    /* Text console state */
B
bellard 已提交
140 141 142 143 144
    int width;
    int height;
    int total_height;
    int backscroll_height;
    int x, y;
145
    int x_saved, y_saved;
B
bellard 已提交
146 147
    int y_displayed;
    int y_base;
148 149
    TextAttributes t_attrib_default; /* default text attributes */
    TextAttributes t_attrib; /* currently active text attributes */
B
bellard 已提交
150
    TextCell *cells;
B
balrog 已提交
151
    int text_x[2], text_y[2], cursor_invalidate;
152
    int echo;
B
bellard 已提交
153

154 155 156 157 158
    int update_x0;
    int update_y0;
    int update_x1;
    int update_y1;

B
bellard 已提交
159 160 161 162
    enum TTYState state;
    int esc_params[MAX_ESC_PARAMS];
    int nb_esc_params;

163
    Chardev *chr;
164 165 166 167
    /* fifo for key pressed */
    QEMUFIFO out_fifo;
    uint8_t out_fifo_buf[16];
    QEMUTimer *kbd_timer;
B
bellard 已提交
168 169
};

170
struct DisplayState {
171
    QEMUTimer *gui_timer;
G
Gerd Hoffmann 已提交
172 173 174
    uint64_t last_update;
    uint64_t update_interval;
    bool refreshing;
175 176 177 178 179 180
    bool have_gfx;
    bool have_text;

    QLIST_HEAD(, DisplayChangeListener) listeners;
};

181
static DisplayState *display_state;
182
static QemuConsole *active_console;
183
static QemuConsole **consoles;
B
bellard 已提交
184
static int nb_consoles = 0;
185 186
static bool cursor_visible_phase;
static QEMUTimer *cursor_timer;
B
bellard 已提交
187

188
static void text_console_do_init(Chardev *chr, DisplayState *ds);
G
Gerd Hoffmann 已提交
189
static void dpy_refresh(DisplayState *s);
190
static DisplayState *get_alloc_displaystate(void);
191 192
static void text_console_update_cursor_timer(void);
static void text_console_update_cursor(void *opaque);
193

194 195
static void gui_update(void *opaque)
{
G
Gerd Hoffmann 已提交
196 197
    uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
    uint64_t dcl_interval;
198 199
    DisplayState *ds = opaque;
    DisplayChangeListener *dcl;
200
    int i;
201

G
Gerd Hoffmann 已提交
202
    ds->refreshing = true;
203
    dpy_refresh(ds);
G
Gerd Hoffmann 已提交
204
    ds->refreshing = false;
205 206

    QLIST_FOREACH(dcl, &ds->listeners, next) {
G
Gerd Hoffmann 已提交
207 208 209 210
        dcl_interval = dcl->update_interval ?
            dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
        if (interval > dcl_interval) {
            interval = dcl_interval;
211 212
        }
    }
G
Gerd Hoffmann 已提交
213 214
    if (ds->update_interval != interval) {
        ds->update_interval = interval;
215 216 217 218 219
        for (i = 0; i < nb_consoles; i++) {
            if (consoles[i]->hw_ops->update_interval) {
                consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval);
            }
        }
G
Gerd Hoffmann 已提交
220 221
        trace_console_refresh(interval);
    }
222 223
    ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
    timer_mod(ds->gui_timer, ds->last_update + interval);
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
}

static void gui_setup_refresh(DisplayState *ds)
{
    DisplayChangeListener *dcl;
    bool need_timer = false;
    bool have_gfx = false;
    bool have_text = false;

    QLIST_FOREACH(dcl, &ds->listeners, next) {
        if (dcl->ops->dpy_refresh != NULL) {
            need_timer = true;
        }
        if (dcl->ops->dpy_gfx_update != NULL) {
            have_gfx = true;
        }
        if (dcl->ops->dpy_text_update != NULL) {
            have_text = true;
        }
    }

    if (need_timer && ds->gui_timer == NULL) {
246 247
        ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
        timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
248 249
    }
    if (!need_timer && ds->gui_timer != NULL) {
250 251
        timer_del(ds->gui_timer);
        timer_free(ds->gui_timer);
252 253 254 255 256 257 258
        ds->gui_timer = NULL;
    }

    ds->have_gfx = have_gfx;
    ds->have_text = have_text;
}

259
void graphic_hw_update(QemuConsole *con)
P
pbrook 已提交
260
{
261 262 263
    if (!con) {
        con = active_console;
    }
G
Gerd Hoffmann 已提交
264 265
    if (con && con->hw_ops->gfx_update) {
        con->hw_ops->gfx_update(con->hw);
266
    }
P
pbrook 已提交
267 268
}

269 270
void graphic_hw_gl_block(QemuConsole *con, bool block)
{
271 272 273 274
    assert(con != NULL);

    con->gl_block = block;
    if (con->hw_ops->gl_block) {
275 276 277 278
        con->hw_ops->gl_block(con->hw, block);
    }
}

279 280 281 282 283 284 285 286 287 288
int qemu_console_get_window_id(QemuConsole *con)
{
    return con->window_id;
}

void qemu_console_set_window_id(QemuConsole *con, int window_id)
{
    con->window_id = window_id;
}

289
void graphic_hw_invalidate(QemuConsole *con)
P
pbrook 已提交
290
{
291 292 293
    if (!con) {
        con = active_console;
    }
G
Gerd Hoffmann 已提交
294 295
    if (con && con->hw_ops->invalidate) {
        con->hw_ops->invalidate(con->hw);
296
    }
P
pbrook 已提交
297 298
}

299
static void ppm_save(const char *filename, DisplaySurface *ds,
G
Gerd Hoffmann 已提交
300
                     Error **errp)
P
pbrook 已提交
301
{
G
Gerd Hoffmann 已提交
302 303
    int width = pixman_image_get_width(ds->image);
    int height = pixman_image_get_height(ds->image);
304
    int fd;
G
Gerd Hoffmann 已提交
305 306 307 308 309 310
    FILE *f;
    int y;
    int ret;
    pixman_image_t *linebuf;

    trace_ppm_save(filename, ds);
311 312
    fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
    if (fd == -1) {
G
Gerd Hoffmann 已提交
313 314 315
        error_setg(errp, "failed to open file '%s': %s", filename,
                   strerror(errno));
        return;
316
    }
317
    f = fdopen(fd, "wb");
G
Gerd Hoffmann 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
    if (ret < 0) {
        linebuf = NULL;
        goto write_err;
    }
    linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
    for (y = 0; y < height; y++) {
        qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
        clearerr(f);
        ret = fwrite(pixman_image_get_data(linebuf), 1,
                     pixman_image_get_stride(linebuf), f);
        (void)ret;
        if (ferror(f)) {
            goto write_err;
        }
333 334
    }

G
Gerd Hoffmann 已提交
335 336 337 338 339 340 341 342 343 344 345 346
out:
    qemu_pixman_image_unref(linebuf);
    fclose(f);
    return;

write_err:
    error_setg(errp, "failed to write to file '%s': %s", filename,
               strerror(errno));
    unlink(filename);
    goto out;
}

347 348
void qmp_screendump(const char *filename, bool has_device, const char *device,
                    bool has_head, int64_t head, Error **errp)
G
Gerd Hoffmann 已提交
349
{
350
    QemuConsole *con;
G
Gerd Hoffmann 已提交
351 352
    DisplaySurface *surface;

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
    if (has_device) {
        con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
                                                 errp);
        if (!con) {
            return;
        }
    } else {
        if (has_head) {
            error_setg(errp, "'head' must be specified together with 'device'");
            return;
        }
        con = qemu_console_lookup_by_index(0);
        if (!con) {
            error_setg(errp, "There is no console to take a screendump from");
            return;
        }
369
    }
G
Gerd Hoffmann 已提交
370 371 372 373

    graphic_hw_update(con);
    surface = qemu_console_surface(con);
    ppm_save(filename, surface, errp);
P
pbrook 已提交
374 375
}

376
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
B
balrog 已提交
377
{
378 379 380
    if (!con) {
        con = active_console;
    }
G
Gerd Hoffmann 已提交
381 382 383
    if (con && con->hw_ops->text_update) {
        con->hw_ops->text_update(con->hw, chardata);
    }
B
balrog 已提交
384 385
}

386 387
static void vga_fill_rect(QemuConsole *con,
                          int posx, int posy, int width, int height,
388
                          pixman_color_t color)
B
bellard 已提交
389
{
390
    DisplaySurface *surface = qemu_console_surface(con);
391 392 393 394 395
    pixman_rectangle16_t rect = {
        .x = posx, .y = posy, .width = width, .height = height
    };

    pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
396
                                 &color, 1, &rect);
B
bellard 已提交
397 398 399
}

/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
400 401
static void vga_bitblt(QemuConsole *con,
                       int xs, int ys, int xd, int yd, int w, int h)
B
bellard 已提交
402
{
403
    DisplaySurface *surface = qemu_console_surface(con);
404 405 406 407

    pixman_image_composite(PIXMAN_OP_SRC,
                           surface->image, NULL, surface->image,
                           xs, ys, 0, 0, xd, yd, w, h);
B
bellard 已提交
408 409 410 411 412 413 414 415 416 417
}

/***********************************************************/
/* basic char display */

#define FONT_HEIGHT 16
#define FONT_WIDTH 8

#include "vgafont.h"

418 419 420 421
#define QEMU_RGB(r, g, b)                                               \
    { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }

static const pixman_color_t color_table_rgb[2][8] = {
422
    {   /* dark */
423 424 425 426 427 428 429 430
        [QEMU_COLOR_BLACK]   = QEMU_RGB(0x00, 0x00, 0x00),  /* black */
        [QEMU_COLOR_BLUE]    = QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
        [QEMU_COLOR_GREEN]   = QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
        [QEMU_COLOR_CYAN]    = QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
        [QEMU_COLOR_RED]     = QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
        [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
        [QEMU_COLOR_YELLOW]  = QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
        [QEMU_COLOR_WHITE]   = QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
431 432
    },
    {   /* bright */
433 434 435 436 437 438 439 440
        [QEMU_COLOR_BLACK]   = QEMU_RGB(0x00, 0x00, 0x00),  /* black */
        [QEMU_COLOR_BLUE]    = QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
        [QEMU_COLOR_GREEN]   = QEMU_RGB(0x00, 0xff, 0x00),  /* green */
        [QEMU_COLOR_CYAN]    = QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
        [QEMU_COLOR_RED]     = QEMU_RGB(0xff, 0x00, 0x00),  /* red */
        [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
        [QEMU_COLOR_YELLOW]  = QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
        [QEMU_COLOR_WHITE]   = QEMU_RGB(0xff, 0xff, 0xff),  /* white */
441
    }
B
bellard 已提交
442 443
};

444
static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
445
                          TextAttributes *t_attrib)
B
bellard 已提交
446
{
447
    static pixman_image_t *glyphs[256];
448
    DisplaySurface *surface = qemu_console_surface(s);
449
    pixman_color_t fgcol, bgcol;
450 451

    if (t_attrib->invers) {
G
Gerd Hoffmann 已提交
452 453
        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
454
    } else {
G
Gerd Hoffmann 已提交
455 456
        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
457
    }
B
bellard 已提交
458

459 460
    if (!glyphs[ch]) {
        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
B
bellard 已提交
461
    }
462
    qemu_pixman_glyph_render(glyphs[ch], surface->image,
463
                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
B
bellard 已提交
464 465
}

466
static void text_console_resize(QemuConsole *s)
B
bellard 已提交
467 468 469 470 471
{
    TextCell *cells, *c, *c1;
    int w1, x, y, last_width;

    last_width = s->width;
G
Gerd Hoffmann 已提交
472 473
    s->width = surface_width(s->surface) / FONT_WIDTH;
    s->height = surface_height(s->surface) / FONT_HEIGHT;
B
bellard 已提交
474 475 476 477 478

    w1 = last_width;
    if (s->width < w1)
        w1 = s->width;

479
    cells = g_new(TextCell, s->width * s->total_height);
B
bellard 已提交
480 481 482 483 484 485 486 487 488 489
    for(y = 0; y < s->total_height; y++) {
        c = &cells[y * s->width];
        if (w1 > 0) {
            c1 = &s->cells[y * last_width];
            for(x = 0; x < w1; x++) {
                *c++ = *c1++;
            }
        }
        for(x = w1; x < s->width; x++) {
            c->ch = ' ';
490
            c->t_attrib = s->t_attrib_default;
B
bellard 已提交
491 492 493
            c++;
        }
    }
494
    g_free(s->cells);
B
bellard 已提交
495 496 497
    s->cells = cells;
}

498
static inline void text_update_xy(QemuConsole *s, int x, int y)
B
balrog 已提交
499 500 501 502 503 504 505
{
    s->text_x[0] = MIN(s->text_x[0], x);
    s->text_x[1] = MAX(s->text_x[1], x);
    s->text_y[0] = MIN(s->text_y[0], y);
    s->text_y[1] = MAX(s->text_y[1], y);
}

506
static void invalidate_xy(QemuConsole *s, int x, int y)
507
{
508 509 510
    if (!qemu_console_is_visible(s)) {
        return;
    }
511 512 513 514 515 516 517 518 519 520
    if (s->update_x0 > x * FONT_WIDTH)
        s->update_x0 = x * FONT_WIDTH;
    if (s->update_y0 > y * FONT_HEIGHT)
        s->update_y0 = y * FONT_HEIGHT;
    if (s->update_x1 < (x + 1) * FONT_WIDTH)
        s->update_x1 = (x + 1) * FONT_WIDTH;
    if (s->update_y1 < (y + 1) * FONT_HEIGHT)
        s->update_y1 = (y + 1) * FONT_HEIGHT;
}

521
static void update_xy(QemuConsole *s, int x, int y)
B
bellard 已提交
522 523 524 525
{
    TextCell *c;
    int y1, y2;

526 527 528
    if (s->ds->have_text) {
        text_update_xy(s, x, y);
    }
B
balrog 已提交
529

530 531 532 533 534 535 536 537 538 539
    y1 = (s->y_base + y) % s->total_height;
    y2 = y1 - s->y_displayed;
    if (y2 < 0) {
        y2 += s->total_height;
    }
    if (y2 < s->height) {
        c = &s->cells[y1 * s->width + x];
        vga_putcharxy(s, x, y2, c->ch,
                      &(c->t_attrib));
        invalidate_xy(s, x, y2);
B
bellard 已提交
540 541 542
    }
}

543
static void console_show_cursor(QemuConsole *s, int show)
B
bellard 已提交
544 545 546
{
    TextCell *c;
    int y, y1;
547
    int x = s->x;
B
bellard 已提交
548

549 550 551
    if (s->ds->have_text) {
        s->cursor_invalidate = 1;
    }
B
balrog 已提交
552

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

574
static void console_refresh(QemuConsole *s)
B
bellard 已提交
575
{
576
    DisplaySurface *surface = qemu_console_surface(s);
B
bellard 已提交
577 578 579
    TextCell *c;
    int x, y, y1;

580
    if (s->ds->have_text) {
B
balrog 已提交
581 582 583 584 585 586
        s->text_x[0] = 0;
        s->text_y[0] = 0;
        s->text_x[1] = s->width - 1;
        s->text_y[1] = s->height - 1;
        s->cursor_invalidate = 1;
    }
B
bellard 已提交
587

588
    vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
589
                  color_table_rgb[0][QEMU_COLOR_BLACK]);
590 591 592 593 594 595 596 597 598 599
    y1 = s->y_displayed;
    for (y = 0; y < s->height; y++) {
        c = s->cells + y1 * s->width;
        for (x = 0; x < s->width; x++) {
            vga_putcharxy(s, x, y, c->ch,
                          &(c->t_attrib));
            c++;
        }
        if (++y1 == s->total_height) {
            y1 = 0;
B
bellard 已提交
600 601
        }
    }
602 603 604
    console_show_cursor(s, 1);
    dpy_gfx_update(s, 0, 0,
                   surface_width(surface), surface_height(surface));
B
bellard 已提交
605 606
}

G
Gerd Hoffmann 已提交
607
static void console_scroll(QemuConsole *s, int ydelta)
B
bellard 已提交
608 609
{
    int i, y1;
610

B
bellard 已提交
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
    if (ydelta > 0) {
        for(i = 0; i < ydelta; i++) {
            if (s->y_displayed == s->y_base)
                break;
            if (++s->y_displayed == s->total_height)
                s->y_displayed = 0;
        }
    } else {
        ydelta = -ydelta;
        i = s->backscroll_height;
        if (i > s->total_height - s->height)
            i = s->total_height - s->height;
        y1 = s->y_base - i;
        if (y1 < 0)
            y1 += s->total_height;
        for(i = 0; i < ydelta; i++) {
            if (s->y_displayed == y1)
                break;
            if (--s->y_displayed < 0)
                s->y_displayed = s->total_height - 1;
        }
    }
    console_refresh(s);
}

636
static void console_put_lf(QemuConsole *s)
B
bellard 已提交
637 638 639 640 641 642 643
{
    TextCell *c;
    int x, y1;

    s->y++;
    if (s->y >= s->height) {
        s->y = s->height - 1;
644

B
bellard 已提交
645 646 647 648 649 650 651 652 653 654 655 656
        if (s->y_displayed == s->y_base) {
            if (++s->y_displayed == s->total_height)
                s->y_displayed = 0;
        }
        if (++s->y_base == s->total_height)
            s->y_base = 0;
        if (s->backscroll_height < s->total_height)
            s->backscroll_height++;
        y1 = (s->y_base + s->height - 1) % s->total_height;
        c = &s->cells[y1 * s->width];
        for(x = 0; x < s->width; x++) {
            c->ch = ' ';
657
            c->t_attrib = s->t_attrib_default;
B
bellard 已提交
658 659
            c++;
        }
660
        if (s->y_displayed == s->y_base) {
661
            if (s->ds->have_text) {
B
balrog 已提交
662 663 664 665 666 667
                s->text_x[0] = 0;
                s->text_y[0] = 0;
                s->text_x[1] = s->width - 1;
                s->text_y[1] = s->height - 1;
            }

668 669 670 671 672 673 674 675 676 677
            vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
                       s->width * FONT_WIDTH,
                       (s->height - 1) * FONT_HEIGHT);
            vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
                          s->width * FONT_WIDTH, FONT_HEIGHT,
                          color_table_rgb[0][s->t_attrib_default.bgcol]);
            s->update_x0 = 0;
            s->update_y0 = 0;
            s->update_x1 = s->width * FONT_WIDTH;
            s->update_y1 = s->height * FONT_HEIGHT;
B
bellard 已提交
678 679 680 681
        }
    }
}

682 683 684 685
/* Set console attributes depending on the current escape codes.
 * NOTE: I know this code is not very efficient (checking every color for it
 * self) but it is more readable and better maintainable.
 */
686
static void console_handle_escape(QemuConsole *s)
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
{
    int i;

    for (i=0; i<s->nb_esc_params; i++) {
        switch (s->esc_params[i]) {
            case 0: /* reset all console attributes to default */
                s->t_attrib = s->t_attrib_default;
                break;
            case 1:
                s->t_attrib.bold = 1;
                break;
            case 4:
                s->t_attrib.uline = 1;
                break;
            case 5:
                s->t_attrib.blink = 1;
                break;
            case 7:
                s->t_attrib.invers = 1;
                break;
            case 8:
                s->t_attrib.unvisible = 1;
                break;
            case 22:
                s->t_attrib.bold = 0;
                break;
            case 24:
                s->t_attrib.uline = 0;
                break;
            case 25:
                s->t_attrib.blink = 0;
                break;
            case 27:
                s->t_attrib.invers = 0;
                break;
            case 28:
                s->t_attrib.unvisible = 0;
                break;
            /* set foreground color */
            case 30:
727
                s->t_attrib.fgcol = QEMU_COLOR_BLACK;
728 729
                break;
            case 31:
730
                s->t_attrib.fgcol = QEMU_COLOR_RED;
731 732
                break;
            case 32:
733
                s->t_attrib.fgcol = QEMU_COLOR_GREEN;
734 735
                break;
            case 33:
736
                s->t_attrib.fgcol = QEMU_COLOR_YELLOW;
737 738
                break;
            case 34:
739
                s->t_attrib.fgcol = QEMU_COLOR_BLUE;
740 741
                break;
            case 35:
742
                s->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
743 744
                break;
            case 36:
745
                s->t_attrib.fgcol = QEMU_COLOR_CYAN;
746 747
                break;
            case 37:
748
                s->t_attrib.fgcol = QEMU_COLOR_WHITE;
749 750 751
                break;
            /* set background color */
            case 40:
752
                s->t_attrib.bgcol = QEMU_COLOR_BLACK;
753 754
                break;
            case 41:
755
                s->t_attrib.bgcol = QEMU_COLOR_RED;
756 757
                break;
            case 42:
758
                s->t_attrib.bgcol = QEMU_COLOR_GREEN;
759 760
                break;
            case 43:
761
                s->t_attrib.bgcol = QEMU_COLOR_YELLOW;
762 763
                break;
            case 44:
764
                s->t_attrib.bgcol = QEMU_COLOR_BLUE;
765 766
                break;
            case 45:
767
                s->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
768 769
                break;
            case 46:
770
                s->t_attrib.bgcol = QEMU_COLOR_CYAN;
771 772
                break;
            case 47:
773
                s->t_attrib.bgcol = QEMU_COLOR_WHITE;
774 775 776 777 778
                break;
        }
    }
}

779
static void console_clear_xy(QemuConsole *s, int x, int y)
780 781 782 783 784 785 786 787
{
    int y1 = (s->y_base + y) % s->total_height;
    TextCell *c = &s->cells[y1 * s->width + x];
    c->ch = ' ';
    c->t_attrib = s->t_attrib_default;
    update_xy(s, x, y);
}

788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
static void console_put_one(QemuConsole *s, int ch)
{
    TextCell *c;
    int y1;
    if (s->x >= s->width) {
        /* line wrap */
        s->x = 0;
        console_put_lf(s);
    }
    y1 = (s->y_base + s->y) % s->total_height;
    c = &s->cells[y1 * s->width + s->x];
    c->ch = ch;
    c->t_attrib = s->t_attrib;
    update_xy(s, s->x, s->y);
    s->x++;
}

static void console_respond_str(QemuConsole *s, const char *buf)
{
    while (*buf) {
        console_put_one(s, *buf);
        buf++;
    }
}

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

    s->x = x;
    s->y = y;
}

833
static void console_putchar(QemuConsole *s, int ch)
B
bellard 已提交
834
{
835
    int i;
836
    int x, y;
837
    char response[40];
B
bellard 已提交
838 839 840 841

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

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

void console_select(unsigned int index)
{
1038
    DisplayChangeListener *dcl;
1039
    QemuConsole *s;
1040

G
Gerd Hoffmann 已提交
1041
    trace_console_select(index);
1042
    s = qemu_console_lookup_by_index(index);
B
bellard 已提交
1043
    if (s) {
1044
        DisplayState *ds = s->ds;
1045

B
bellard 已提交
1046
        active_console = s;
1047
        if (ds->have_gfx) {
1048 1049 1050 1051 1052 1053 1054 1055
            QLIST_FOREACH(dcl, &ds->listeners, next) {
                if (dcl->con != NULL) {
                    continue;
                }
                if (dcl->ops->dpy_gfx_switch) {
                    dcl->ops->dpy_gfx_switch(dcl, s->surface);
                }
            }
1056 1057 1058 1059
            if (s->surface) {
                dpy_gfx_update(s, 0, 0, surface_width(s->surface),
                               surface_height(s->surface));
            }
1060 1061
        }
        if (ds->have_text) {
1062
            dpy_text_resize(s, s->width, s->height);
1063
        }
1064
        text_console_update_cursor(NULL);
B
bellard 已提交
1065 1066 1067
    }
}

1068 1069
typedef struct VCChardev {
    Chardev parent;
1070
    QemuConsole *console;
1071
} VCChardev;
1072

M
Marc-André Lureau 已提交
1073 1074 1075
#define TYPE_CHARDEV_VC "chardev-vc"
#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)

1076
static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
B
bellard 已提交
1077
{
M
Marc-André Lureau 已提交
1078
    VCChardev *drv = VC_CHARDEV(chr);
1079
    QemuConsole *s = drv->console;
B
bellard 已提交
1080 1081
    int i;

1082 1083 1084 1085
    if (!s->ds) {
        return 0;
    }

1086 1087 1088 1089
    s->update_x0 = s->width * FONT_WIDTH;
    s->update_y0 = s->height * FONT_HEIGHT;
    s->update_x1 = 0;
    s->update_y1 = 0;
B
bellard 已提交
1090 1091 1092 1093 1094
    console_show_cursor(s, 0);
    for(i = 0; i < len; i++) {
        console_putchar(s, buf[i]);
    }
    console_show_cursor(s, 1);
1095
    if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
1096
        dpy_gfx_update(s, s->update_x0, s->update_y0,
1097 1098
                       s->update_x1 - s->update_x0,
                       s->update_y1 - s->update_y0);
1099
    }
B
bellard 已提交
1100 1101 1102
    return len;
}

1103 1104
static void kbd_send_chars(void *opaque)
{
1105
    QemuConsole *s = opaque;
1106 1107
    int len;
    uint8_t buf[16];
1108

1109
    len = qemu_chr_be_can_write(s->chr);
1110 1111 1112 1113 1114 1115
    if (len > s->out_fifo.count)
        len = s->out_fifo.count;
    if (len > 0) {
        if (len > sizeof(buf))
            len = sizeof(buf);
        qemu_fifo_read(&s->out_fifo, buf, len);
1116
        qemu_chr_be_write(s->chr, buf, len);
1117 1118 1119 1120
    }
    /* characters are pending: we send them a bit later (XXX:
       horrible, should change char device API) */
    if (s->out_fifo.count > 0) {
1121
        timer_mod(s->kbd_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1);
1122 1123 1124
    }
}

B
bellard 已提交
1125
/* called when an ascii key is pressed */
1126
void kbd_put_keysym_console(QemuConsole *s, int keysym)
B
bellard 已提交
1127 1128
{
    uint8_t buf[16], *q;
1129
    CharBackend *be;
B
bellard 已提交
1130 1131
    int c;

1132
    if (!s || (s->console_type == GRAPHIC_CONSOLE))
B
bellard 已提交
1133 1134 1135 1136
        return;

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

1181
static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
1182 1183 1184 1185 1186 1187 1188 1189 1190
    [Q_KEY_CODE_UP]     = QEMU_KEY_UP,
    [Q_KEY_CODE_DOWN]   = QEMU_KEY_DOWN,
    [Q_KEY_CODE_RIGHT]  = QEMU_KEY_RIGHT,
    [Q_KEY_CODE_LEFT]   = QEMU_KEY_LEFT,
    [Q_KEY_CODE_HOME]   = QEMU_KEY_HOME,
    [Q_KEY_CODE_END]    = QEMU_KEY_END,
    [Q_KEY_CODE_PGUP]   = QEMU_KEY_PAGEUP,
    [Q_KEY_CODE_PGDN]   = QEMU_KEY_PAGEDOWN,
    [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
1191
    [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
1192 1193
};

1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
    [Q_KEY_CODE_UP]     = QEMU_KEY_CTRL_UP,
    [Q_KEY_CODE_DOWN]   = QEMU_KEY_CTRL_DOWN,
    [Q_KEY_CODE_RIGHT]  = QEMU_KEY_CTRL_RIGHT,
    [Q_KEY_CODE_LEFT]   = QEMU_KEY_CTRL_LEFT,
    [Q_KEY_CODE_HOME]   = QEMU_KEY_CTRL_HOME,
    [Q_KEY_CODE_END]    = QEMU_KEY_CTRL_END,
    [Q_KEY_CODE_PGUP]   = QEMU_KEY_CTRL_PAGEUP,
    [Q_KEY_CODE_PGDN]   = QEMU_KEY_CTRL_PAGEDOWN,
};

bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
1206 1207 1208
{
    int keysym;

1209
    keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
1210 1211 1212 1213 1214 1215 1216
    if (keysym == 0) {
        return false;
    }
    kbd_put_keysym_console(s, keysym);
    return true;
}

1217 1218 1219 1220 1221 1222 1223 1224 1225
void kbd_put_string_console(QemuConsole *s, const char *str, int len)
{
    int i;

    for (i = 0; i < len && str[i]; i++) {
        kbd_put_keysym_console(s, str[i]);
    }
}

1226 1227 1228 1229 1230
void kbd_put_keysym(int keysym)
{
    kbd_put_keysym_console(active_console, keysym);
}

B
balrog 已提交
1231 1232
static void text_console_invalidate(void *opaque)
{
1233
    QemuConsole *s = (QemuConsole *) opaque;
1234 1235

    if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1236 1237
        text_console_resize(s);
    }
B
balrog 已提交
1238 1239 1240
    console_refresh(s);
}

A
Anthony Liguori 已提交
1241
static void text_console_update(void *opaque, console_ch_t *chardata)
B
balrog 已提交
1242
{
1243
    QemuConsole *s = (QemuConsole *) opaque;
B
balrog 已提交
1244 1245 1246 1247 1248 1249
    int i, j, src;

    if (s->text_x[0] <= s->text_x[1]) {
        src = (s->y_base + s->text_y[0]) * s->width;
        chardata += s->text_y[0] * s->width;
        for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1250 1251 1252 1253 1254 1255 1256
            for (j = 0; j < s->width; j++, src++) {
                console_write_ch(chardata ++,
                                 ATTR2CHTYPE(s->cells[src].ch,
                                             s->cells[src].t_attrib.fgcol,
                                             s->cells[src].t_attrib.bgcol,
                                             s->cells[src].t_attrib.bold));
            }
1257
        dpy_text_update(s, s->text_x[0], s->text_y[0],
1258
                        s->text_x[1] - s->text_x[0], i - s->text_y[0]);
B
balrog 已提交
1259 1260 1261 1262 1263 1264
        s->text_x[0] = s->width;
        s->text_y[0] = s->height;
        s->text_x[1] = 0;
        s->text_y[1] = 0;
    }
    if (s->cursor_invalidate) {
1265
        dpy_text_cursor(s, s->x, s->y);
B
balrog 已提交
1266 1267 1268 1269
        s->cursor_invalidate = 0;
    }
}

1270 1271
static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
                                uint32_t head)
B
bellard 已提交
1272
{
G
Gerd Hoffmann 已提交
1273
    Object *obj;
1274
    QemuConsole *s;
P
pbrook 已提交
1275
    int i;
B
bellard 已提交
1276

G
Gerd Hoffmann 已提交
1277 1278
    obj = object_new(TYPE_QEMU_CONSOLE);
    s = QEMU_CONSOLE(obj);
1279
    s->head = head;
1280
    object_property_add_link(obj, "device", TYPE_DEVICE,
1281
                             (Object **)&s->device,
1282
                             object_property_allow_set_link,
1283
                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
1284
                             &error_abort);
1285
    object_property_add_uint32_ptr(obj, "head",
1286
                                   &s->head, &error_abort);
1287

1288 1289
    if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
        (console_type == GRAPHIC_CONSOLE))) {
B
bellard 已提交
1290
        active_console = s;
1291
    }
B
bellard 已提交
1292
    s->ds = ds;
1293
    s->console_type = console_type;
1294 1295

    consoles = g_realloc(consoles, sizeof(*consoles) * (nb_consoles+1));
G
Gerd Hoffmann 已提交
1296
    if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) {
1297
        s->index = nb_consoles;
P
pbrook 已提交
1298 1299
        consoles[nb_consoles++] = s;
    } else {
G
Gerd Hoffmann 已提交
1300 1301 1302 1303 1304 1305
        /*
         * HACK: Put graphical consoles before text consoles.
         *
         * Only do that for coldplugged devices.  After initial device
         * initialization we will not renumber the consoles any more.
         */
P
pbrook 已提交
1306
        for (i = nb_consoles; i > 0; i--) {
1307
            if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
P
pbrook 已提交
1308 1309
                break;
            consoles[i] = consoles[i - 1];
1310
            consoles[i]->index = i;
P
pbrook 已提交
1311
        }
1312
        s->index = i;
P
pbrook 已提交
1313
        consoles[i] = s;
1314
        nb_consoles++;
P
pbrook 已提交
1315 1316 1317 1318
    }
    return s;
}

G
Gerd Hoffmann 已提交
1319
static void qemu_alloc_display(DisplaySurface *surface, int width, int height)
1320
{
1321 1322 1323
    qemu_pixman_image_unref(surface->image);
    surface->image = NULL;

G
Gerd Hoffmann 已提交
1324
    surface->format = PIXMAN_x8r8g8b8;
1325 1326
    surface->image = pixman_image_create_bits(surface->format,
                                              width, height,
G
Gerd Hoffmann 已提交
1327
                                              NULL, width * 4);
1328 1329
    assert(surface->image != NULL);

G
Gerd Hoffmann 已提交
1330
    surface->flags = QEMU_ALLOCATED_FLAG;
1331 1332
}

1333
DisplaySurface *qemu_create_displaysurface(int width, int height)
1334 1335
{
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1336 1337

    trace_displaysurface_create(surface, width, height);
G
Gerd Hoffmann 已提交
1338
    qemu_alloc_display(surface, width, height);
1339 1340 1341
    return surface;
}

G
Gerd Hoffmann 已提交
1342 1343 1344
DisplaySurface *qemu_create_displaysurface_from(int width, int height,
                                                pixman_format_code_t format,
                                                int linesize, uint8_t *data)
1345
{
1346
    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1347

G
Gerd Hoffmann 已提交
1348 1349
    trace_displaysurface_create_from(surface, width, height, format);
    surface->format = format;
1350 1351 1352 1353 1354
    surface->image = pixman_image_create_bits(surface->format,
                                              width, height,
                                              (void *)data, linesize);
    assert(surface->image != NULL);

1355 1356 1357
    return surface;
}

1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368
DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
{
    DisplaySurface *surface = g_new0(DisplaySurface, 1);

    trace_displaysurface_create_pixman(surface);
    surface->format = pixman_image_get_format(image);
    surface->image = pixman_image_ref(image);

    return surface;
}

1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389
static void qemu_unmap_displaysurface_guestmem(pixman_image_t *image,
                                               void *unused)
{
    void *data = pixman_image_get_data(image);
    uint32_t size = pixman_image_get_stride(image) *
        pixman_image_get_height(image);
    cpu_physical_memory_unmap(data, size, 0, 0);
}

DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height,
                                                    pixman_format_code_t format,
                                                    int linesize, uint64_t addr)
{
    DisplaySurface *surface;
    hwaddr size;
    void *data;

    if (linesize == 0) {
        linesize = width * PIXMAN_FORMAT_BPP(format) / 8;
    }

G
Gonglei 已提交
1390
    size = (hwaddr)linesize * height;
1391
    data = cpu_physical_memory_map(addr, &size, 0);
G
Gonglei 已提交
1392
    if (size != (hwaddr)linesize * height) {
1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404
        cpu_physical_memory_unmap(data, size, 0, 0);
        return NULL;
    }

    surface = qemu_create_displaysurface_from
        (width, height, format, linesize, data);
    pixman_image_set_destroy_function
        (surface->image, qemu_unmap_displaysurface_guestmem, NULL);

    return surface;
}

1405 1406
DisplaySurface *qemu_create_message_surface(int w, int h,
                                            const char *msg)
1407
{
G
Gerd Hoffmann 已提交
1408
    DisplaySurface *surface = qemu_create_displaysurface(w, h);
1409 1410
    pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
    pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
1411 1412 1413 1414
    pixman_image_t *glyph;
    int len, x, y, i;

    len = strlen(msg);
G
Gerd Hoffmann 已提交
1415 1416
    x = (w / FONT_WIDTH  - len) / 2;
    y = (h / FONT_HEIGHT - 1)   / 2;
1417 1418 1419 1420 1421 1422 1423 1424 1425
    for (i = 0; i < len; i++) {
        glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
        qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
                                 x+i, y, FONT_WIDTH, FONT_HEIGHT);
        qemu_pixman_image_unref(glyph);
    }
    return surface;
}

1426
void qemu_free_displaysurface(DisplaySurface *surface)
1427
{
1428
    if (surface == NULL) {
1429
        return;
G
Gerd Hoffmann 已提交
1430
    }
1431 1432 1433
    trace_displaysurface_free(surface);
    qemu_pixman_image_unref(surface->image);
    g_free(surface);
1434 1435
}

1436 1437 1438 1439 1440
bool console_has_gl(QemuConsole *con)
{
    return con->gl != NULL;
}

G
Gerd Hoffmann 已提交
1441 1442 1443 1444 1445
bool console_has_gl_dmabuf(QemuConsole *con)
{
    return con->gl != NULL && con->gl->ops->dpy_gl_scanout_dmabuf != NULL;
}

1446
void register_displaychangelistener(DisplayChangeListener *dcl)
1447
{
G
Gerd Hoffmann 已提交
1448 1449
    static const char nodev[] =
        "This VM has no graphic display device.";
1450
    static DisplaySurface *dummy;
1451 1452
    QemuConsole *con;

1453 1454
    assert(!dcl->ds);

1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
    if (dcl->ops->dpy_gl_ctx_create) {
        /* display has opengl support */
        assert(dcl->con);
        if (dcl->con->gl) {
            fprintf(stderr, "can't register two opengl displays (%s, %s)\n",
                    dcl->ops->dpy_name, dcl->con->gl->ops->dpy_name);
            exit(1);
        }
        dcl->con->gl = dcl;
    }

1466
    trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1467 1468 1469
    dcl->ds = get_alloc_displaystate();
    QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
    gui_setup_refresh(dcl->ds);
1470 1471 1472 1473 1474 1475
    if (dcl->con) {
        dcl->con->dcls++;
        con = dcl->con;
    } else {
        con = active_console;
    }
1476 1477 1478 1479 1480
    if (dcl->ops->dpy_gfx_switch) {
        if (con) {
            dcl->ops->dpy_gfx_switch(dcl, con->surface);
        } else {
            if (!dummy) {
G
Gerd Hoffmann 已提交
1481
                dummy = qemu_create_message_surface(640, 480, nodev);
1482 1483 1484
            }
            dcl->ops->dpy_gfx_switch(dcl, dummy);
        }
1485
    }
1486
    text_console_update_cursor(NULL);
1487 1488
}

G
Gerd Hoffmann 已提交
1489 1490 1491 1492 1493 1494 1495
void update_displaychangelistener(DisplayChangeListener *dcl,
                                  uint64_t interval)
{
    DisplayState *ds = dcl->ds;

    dcl->update_interval = interval;
    if (!ds->refreshing && ds->update_interval > interval) {
1496
        timer_mod(ds->gui_timer, ds->last_update + interval);
G
Gerd Hoffmann 已提交
1497 1498 1499
    }
}

1500 1501 1502 1503
void unregister_displaychangelistener(DisplayChangeListener *dcl)
{
    DisplayState *ds = dcl->ds;
    trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1504 1505 1506
    if (dcl->con) {
        dcl->con->dcls--;
    }
1507
    QLIST_REMOVE(dcl, next);
G
Gerd Hoffmann 已提交
1508
    dcl->ds = NULL;
1509 1510 1511
    gui_setup_refresh(ds);
}

1512 1513 1514 1515 1516 1517 1518
static void dpy_set_ui_info_timer(void *opaque)
{
    QemuConsole *con = opaque;

    con->hw_ops->ui_info(con->hw, con->head, &con->ui_info);
}

1519 1520 1521 1522 1523
bool dpy_ui_info_supported(QemuConsole *con)
{
    return con->hw_ops->ui_info != NULL;
}

G
Gerd Hoffmann 已提交
1524 1525 1526
int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info)
{
    assert(con != NULL);
1527

1528
    if (!dpy_ui_info_supported(con)) {
1529
        return -1;
G
Gerd Hoffmann 已提交
1530
    }
1531 1532 1533 1534
    if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
        /* nothing changed -- ignore */
        return 0;
    }
1535 1536 1537 1538 1539 1540

    /*
     * Typically we get a flood of these as the user resizes the window.
     * Wait until the dust has settled (one second without updates), then
     * go notify the guest.
     */
1541
    con->ui_info = *info;
1542 1543
    timer_mod(con->ui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
    return 0;
G
Gerd Hoffmann 已提交
1544 1545
}

1546
void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1547
{
1548
    DisplayState *s = con->ds;
1549
    DisplayChangeListener *dcl;
1550 1551
    int width = w;
    int height = h;
1552

1553 1554 1555 1556
    if (con->surface) {
        width = surface_width(con->surface);
        height = surface_height(con->surface);
    }
1557 1558 1559 1560 1561 1562 1563
    x = MAX(x, 0);
    y = MAX(y, 0);
    x = MIN(x, width);
    y = MIN(y, height);
    w = MIN(w, width - x);
    h = MIN(h, height - y);

G
Gerd Hoffmann 已提交
1564
    if (!qemu_console_is_visible(con)) {
1565 1566
        return;
    }
1567
    QLIST_FOREACH(dcl, &s->listeners, next) {
1568 1569 1570
        if (con != (dcl->con ? dcl->con : active_console)) {
            continue;
        }
1571
        if (dcl->ops->dpy_gfx_update) {
1572
            dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1573 1574 1575 1576
        }
    }
}

1577 1578 1579 1580 1581
void dpy_gfx_replace_surface(QemuConsole *con,
                             DisplaySurface *surface)
{
    DisplayState *s = con->ds;
    DisplaySurface *old_surface = con->surface;
1582
    DisplayChangeListener *dcl;
1583

1584
    assert(old_surface != surface || surface == NULL);
1585

1586
    con->surface = surface;
1587 1588 1589 1590 1591 1592 1593
    QLIST_FOREACH(dcl, &s->listeners, next) {
        if (con != (dcl->con ? dcl->con : active_console)) {
            continue;
        }
        if (dcl->ops->dpy_gfx_switch) {
            dcl->ops->dpy_gfx_switch(dcl, surface);
        }
1594
    }
1595
    qemu_free_displaysurface(old_surface);
1596 1597
}

1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
bool dpy_gfx_check_format(QemuConsole *con,
                          pixman_format_code_t format)
{
    DisplayChangeListener *dcl;
    DisplayState *s = con->ds;

    QLIST_FOREACH(dcl, &s->listeners, next) {
        if (dcl->con && dcl->con != con) {
            /* dcl bound to another console -> skip */
            continue;
        }
        if (dcl->ops->dpy_gfx_check_format) {
            if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
                return false;
            }
        } else {
            /* default is to whitelist native 32 bpp only */
            if (format != qemu_default_pixman_format(32, true)) {
                return false;
            }
        }
    }
    return true;
}

S
Stefan Weil 已提交
1623
static void dpy_refresh(DisplayState *s)
1624
{
1625 1626
    DisplayChangeListener *dcl;

1627 1628
    QLIST_FOREACH(dcl, &s->listeners, next) {
        if (dcl->ops->dpy_refresh) {
1629
            dcl->ops->dpy_refresh(dcl);
1630 1631 1632 1633
        }
    }
}

1634
void dpy_text_cursor(QemuConsole *con, int x, int y)
1635
{
1636
    DisplayState *s = con->ds;
1637
    DisplayChangeListener *dcl;
1638

G
Gerd Hoffmann 已提交
1639
    if (!qemu_console_is_visible(con)) {
1640 1641
        return;
    }
1642
    QLIST_FOREACH(dcl, &s->listeners, next) {
1643 1644 1645
        if (con != (dcl->con ? dcl->con : active_console)) {
            continue;
        }
1646
        if (dcl->ops->dpy_text_cursor) {
1647
            dcl->ops->dpy_text_cursor(dcl, x, y);
1648 1649 1650 1651
        }
    }
}

1652
void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1653
{
1654
    DisplayState *s = con->ds;
1655
    DisplayChangeListener *dcl;
1656

G
Gerd Hoffmann 已提交
1657
    if (!qemu_console_is_visible(con)) {
1658 1659
        return;
    }
1660
    QLIST_FOREACH(dcl, &s->listeners, next) {
1661 1662 1663
        if (con != (dcl->con ? dcl->con : active_console)) {
            continue;
        }
1664
        if (dcl->ops->dpy_text_update) {
1665
            dcl->ops->dpy_text_update(dcl, x, y, w, h);
1666 1667 1668 1669
        }
    }
}

1670
void dpy_text_resize(QemuConsole *con, int w, int h)
1671
{
1672
    DisplayState *s = con->ds;
1673
    DisplayChangeListener *dcl;
1674

G
Gerd Hoffmann 已提交
1675
    if (!qemu_console_is_visible(con)) {
1676 1677
        return;
    }
1678
    QLIST_FOREACH(dcl, &s->listeners, next) {
1679 1680 1681
        if (con != (dcl->con ? dcl->con : active_console)) {
            continue;
        }
1682
        if (dcl->ops->dpy_text_resize) {
1683
            dcl->ops->dpy_text_resize(dcl, w, h);
1684 1685 1686 1687
        }
    }
}

1688
void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1689
{
1690
    DisplayState *s = con->ds;
1691
    DisplayChangeListener *dcl;
1692

G
Gerd Hoffmann 已提交
1693
    if (!qemu_console_is_visible(con)) {
1694 1695
        return;
    }
1696
    QLIST_FOREACH(dcl, &s->listeners, next) {
1697 1698 1699
        if (con != (dcl->con ? dcl->con : active_console)) {
            continue;
        }
1700
        if (dcl->ops->dpy_mouse_set) {
1701
            dcl->ops->dpy_mouse_set(dcl, x, y, on);
1702 1703 1704 1705
        }
    }
}

1706
void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1707
{
1708
    DisplayState *s = con->ds;
1709
    DisplayChangeListener *dcl;
1710

G
Gerd Hoffmann 已提交
1711
    if (!qemu_console_is_visible(con)) {
1712 1713
        return;
    }
1714
    QLIST_FOREACH(dcl, &s->listeners, next) {
1715 1716 1717
        if (con != (dcl->con ? dcl->con : active_console)) {
            continue;
        }
1718
        if (dcl->ops->dpy_cursor_define) {
1719
            dcl->ops->dpy_cursor_define(dcl, cursor);
1720 1721 1722 1723
        }
    }
}

1724
bool dpy_cursor_define_supported(QemuConsole *con)
1725
{
1726
    DisplayState *s = con->ds;
1727 1728
    DisplayChangeListener *dcl;

1729 1730 1731 1732 1733 1734 1735 1736
    QLIST_FOREACH(dcl, &s->listeners, next) {
        if (dcl->ops->dpy_cursor_define) {
            return true;
        }
    }
    return false;
}

1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761
QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
                                struct QEMUGLParams *qparams)
{
    assert(con->gl);
    return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
}

void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
{
    assert(con->gl);
    con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
}

int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
{
    assert(con->gl);
    return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
}

QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con)
{
    assert(con->gl);
    return con->gl->ops->dpy_gl_ctx_get_current(con->gl);
}

1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772
void dpy_gl_scanout_disable(QemuConsole *con)
{
    assert(con->gl);
    if (con->gl->ops->dpy_gl_scanout_disable) {
        con->gl->ops->dpy_gl_scanout_disable(con->gl);
    } else {
        con->gl->ops->dpy_gl_scanout_texture(con->gl, 0, false, 0, 0,
                                             0, 0, 0, 0);
    }
}

1773 1774 1775 1776 1777 1778 1779
void dpy_gl_scanout_texture(QemuConsole *con,
                            uint32_t backing_id,
                            bool backing_y_0_top,
                            uint32_t backing_width,
                            uint32_t backing_height,
                            uint32_t x, uint32_t y,
                            uint32_t width, uint32_t height)
1780 1781
{
    assert(con->gl);
1782 1783 1784 1785
    con->gl->ops->dpy_gl_scanout_texture(con->gl, backing_id,
                                         backing_y_0_top,
                                         backing_width, backing_height,
                                         x, y, width, height);
1786 1787
}

G
Gerd Hoffmann 已提交
1788 1789 1790 1791 1792 1793 1794
void dpy_gl_scanout_dmabuf(QemuConsole *con,
                           QemuDmaBuf *dmabuf)
{
    assert(con->gl);
    con->gl->ops->dpy_gl_scanout_dmabuf(con->gl, dmabuf);
}

1795 1796
void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
                          bool have_hot, uint32_t hot_x, uint32_t hot_y)
G
Gerd Hoffmann 已提交
1797 1798 1799 1800
{
    assert(con->gl);

    if (con->gl->ops->dpy_gl_cursor_dmabuf) {
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
        con->gl->ops->dpy_gl_cursor_dmabuf(con->gl, dmabuf,
                                           have_hot, hot_x, hot_y);
    }
}

void dpy_gl_cursor_position(QemuConsole *con,
                            uint32_t pos_x, uint32_t pos_y)
{
    assert(con->gl);

    if (con->gl->ops->dpy_gl_cursor_position) {
        con->gl->ops->dpy_gl_cursor_position(con->gl, pos_x, pos_y);
G
Gerd Hoffmann 已提交
1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825
    }
}

void dpy_gl_release_dmabuf(QemuConsole *con,
                          QemuDmaBuf *dmabuf)
{
    assert(con->gl);

    if (con->gl->ops->dpy_gl_release_dmabuf) {
        con->gl->ops->dpy_gl_release_dmabuf(con->gl, dmabuf);
    }
}

1826 1827 1828 1829 1830 1831 1832
void dpy_gl_update(QemuConsole *con,
                   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
    assert(con->gl);
    con->gl->ops->dpy_gl_update(con->gl, x, y, w, h);
}

1833 1834 1835
/***********************************************************/
/* register display */

1836 1837
/* console.c internal use only */
static DisplayState *get_alloc_displaystate(void)
1838
{
1839 1840
    if (!display_state) {
        display_state = g_new0(DisplayState, 1);
1841 1842
        cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
                                    text_console_update_cursor, NULL);
1843 1844
    }
    return display_state;
1845 1846
}

1847 1848 1849 1850 1851
/*
 * Called by main(), after creating QemuConsoles
 * and before initializing ui (sdl/vnc/...).
 */
DisplayState *init_displaystate(void)
1852
{
1853
    gchar *name;
1854 1855
    int i;

1856
    get_alloc_displaystate();
1857 1858 1859 1860 1861
    for (i = 0; i < nb_consoles; i++) {
        if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
            consoles[i]->ds == NULL) {
            text_console_do_init(consoles[i]->chr, display_state);
        }
1862 1863 1864 1865 1866 1867

        /* Hook up into the qom tree here (not in new_console()), once
         * all QemuConsoles are created and the order / numbering
         * doesn't change any more */
        name = g_strdup_printf("console[%d]", i);
        object_property_add_child(container_get(object_get_root(), "/backend"),
1868
                                  name, OBJECT(consoles[i]), &error_abort);
1869
        g_free(name);
1870 1871
    }

1872 1873 1874
    return display_state;
}

1875 1876 1877 1878 1879 1880 1881 1882
void graphic_console_set_hwops(QemuConsole *con,
                               const GraphicHwOps *hw_ops,
                               void *opaque)
{
    con->hw_ops = hw_ops;
    con->hw = opaque;
}

1883
QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
1884
                                  const GraphicHwOps *hw_ops,
1885
                                  void *opaque)
P
pbrook 已提交
1886
{
G
Gerd Hoffmann 已提交
1887 1888
    static const char noinit[] =
        "Guest has not initialized the display (yet).";
1889 1890
    int width = 640;
    int height = 480;
1891
    QemuConsole *s;
1892
    DisplayState *ds;
G
Gerd Hoffmann 已提交
1893
    DisplaySurface *surface;
A
aurel32 已提交
1894

1895
    ds = get_alloc_displaystate();
G
Gerd Hoffmann 已提交
1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908
    s = qemu_console_lookup_unused();
    if (s) {
        trace_console_gfx_reuse(s->index);
        if (s->surface) {
            width = surface_width(s->surface);
            height = surface_height(s->surface);
        }
    } else {
        trace_console_gfx_new();
        s = new_console(ds, GRAPHIC_CONSOLE, head);
        s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
                                   dpy_set_ui_info_timer, s);
    }
1909
    graphic_console_set_hwops(s, hw_ops, opaque);
1910
    if (dev) {
1911 1912
        object_property_set_link(OBJECT(s), OBJECT(dev), "device",
                                 &error_abort);
1913
    }
1914

G
Gerd Hoffmann 已提交
1915 1916
    surface = qemu_create_message_surface(width, height, noinit);
    dpy_gfx_replace_surface(s, surface);
1917
    return s;
B
bellard 已提交
1918 1919
}

G
Gerd Hoffmann 已提交
1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947
static const GraphicHwOps unused_ops = {
    /* no callbacks */
};

void graphic_console_close(QemuConsole *con)
{
    static const char unplugged[] =
        "Guest display has been unplugged";
    DisplaySurface *surface;
    int width = 640;
    int height = 480;

    if (con->surface) {
        width = surface_width(con->surface);
        height = surface_height(con->surface);
    }

    trace_console_gfx_close(con->index);
    object_property_set_link(OBJECT(con), NULL, "device", &error_abort);
    graphic_console_set_hwops(con, &unused_ops, NULL);

    if (con->gl) {
        dpy_gl_scanout_disable(con);
    }
    surface = qemu_create_message_surface(width, height, unplugged);
    dpy_gfx_replace_surface(con, surface);
}

1948 1949
QemuConsole *qemu_console_lookup_by_index(unsigned int index)
{
1950
    if (index >= nb_consoles) {
1951 1952 1953 1954 1955
        return NULL;
    }
    return consoles[index];
}

1956
QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
1957 1958
{
    Object *obj;
1959
    uint32_t h;
1960 1961 1962 1963 1964 1965 1966
    int i;

    for (i = 0; i < nb_consoles; i++) {
        if (!consoles[i]) {
            continue;
        }
        obj = object_property_get_link(OBJECT(consoles[i]),
1967
                                       "device", &error_abort);
1968 1969 1970
        if (DEVICE(obj) != dev) {
            continue;
        }
1971 1972
        h = object_property_get_uint(OBJECT(consoles[i]),
                                     "head", &error_abort);
1973 1974
        if (h != head) {
            continue;
1975
        }
1976
        return consoles[i];
1977 1978 1979 1980
    }
    return NULL;
}

1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003
QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
                                                uint32_t head, Error **errp)
{
    DeviceState *dev;
    QemuConsole *con;

    dev = qdev_find_recursive(sysbus_get_default(), device_id);
    if (dev == NULL) {
        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                  "Device '%s' not found", device_id);
        return NULL;
    }

    con = qemu_console_lookup_by_device(dev, head);
    if (con == NULL) {
        error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
                   device_id, head);
        return NULL;
    }

    return con;
}

G
Gerd Hoffmann 已提交
2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025
QemuConsole *qemu_console_lookup_unused(void)
{
    Object *obj;
    int i;

    for (i = 0; i < nb_consoles; i++) {
        if (!consoles[i]) {
            continue;
        }
        if (consoles[i]->hw_ops != &unused_ops) {
            continue;
        }
        obj = object_property_get_link(OBJECT(consoles[i]),
                                       "device", &error_abort);
        if (obj != NULL) {
            continue;
        }
        return consoles[i];
    }
    return NULL;
}

G
Gerd Hoffmann 已提交
2026
bool qemu_console_is_visible(QemuConsole *con)
B
bellard 已提交
2027
{
2028
    return (con == active_console) || (con->dcls > 0);
B
bellard 已提交
2029 2030
}

G
Gerd Hoffmann 已提交
2031
bool qemu_console_is_graphic(QemuConsole *con)
2032
{
G
Gerd Hoffmann 已提交
2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044
    if (con == NULL) {
        con = active_console;
    }
    return con && (con->console_type == GRAPHIC_CONSOLE);
}

bool qemu_console_is_fixedsize(QemuConsole *con)
{
    if (con == NULL) {
        con = active_console;
    }
    return con && (con->console_type != TEXT_CONSOLE);
2045 2046
}

2047 2048 2049 2050 2051 2052
bool qemu_console_is_gl_blocked(QemuConsole *con)
{
    assert(con != NULL);
    return con->gl_block;
}

2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067
char *qemu_console_get_label(QemuConsole *con)
{
    if (con->console_type == GRAPHIC_CONSOLE) {
        if (con->device) {
            return g_strdup(object_get_typename(con->device));
        }
        return g_strdup("VGA");
    } else {
        if (con->chr && con->chr->label) {
            return g_strdup(con->chr->label);
        }
        return g_strdup_printf("vc%d", con->index);
    }
}

2068 2069 2070 2071 2072 2073 2074 2075
int qemu_console_get_index(QemuConsole *con)
{
    if (con == NULL) {
        con = active_console;
    }
    return con ? con->index : -1;
}

2076 2077 2078 2079 2080 2081 2082 2083
uint32_t qemu_console_get_head(QemuConsole *con)
{
    if (con == NULL) {
        con = active_console;
    }
    return con ? con->head : -1;
}

G
Gerd Hoffmann 已提交
2084 2085 2086 2087 2088 2089
QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con)
{
    assert(con != NULL);
    return &con->ui_info;
}

2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105
int qemu_console_get_width(QemuConsole *con, int fallback)
{
    if (con == NULL) {
        con = active_console;
    }
    return con ? surface_width(con->surface) : fallback;
}

int qemu_console_get_height(QemuConsole *con, int fallback)
{
    if (con == NULL) {
        con = active_console;
    }
    return con ? surface_height(con->surface) : fallback;
}

2106
static void vc_chr_set_echo(Chardev *chr, bool echo)
2107
{
M
Marc-André Lureau 已提交
2108
    VCChardev *drv = VC_CHARDEV(chr);
2109
    QemuConsole *s = drv->console;
2110 2111 2112 2113

    s->echo = echo;
}

2114 2115 2116 2117 2118 2119
static void text_console_update_cursor_timer(void)
{
    timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
              + CONSOLE_CURSOR_PERIOD / 2);
}

2120 2121
static void text_console_update_cursor(void *opaque)
{
2122 2123 2124 2125
    QemuConsole *s;
    int i, count = 0;

    cursor_visible_phase = !cursor_visible_phase;
2126

2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139
    for (i = 0; i < nb_consoles; i++) {
        s = consoles[i];
        if (qemu_console_is_graphic(s) ||
            !qemu_console_is_visible(s)) {
            continue;
        }
        count++;
        graphic_hw_invalidate(s);
    }

    if (count) {
        text_console_update_cursor_timer();
    }
2140 2141
}

G
Gerd Hoffmann 已提交
2142 2143 2144 2145 2146
static const GraphicHwOps text_console_ops = {
    .invalidate  = text_console_invalidate,
    .text_update = text_console_update,
};

2147
static void text_console_do_init(Chardev *chr, DisplayState *ds)
B
bellard 已提交
2148
{
M
Marc-André Lureau 已提交
2149
    VCChardev *drv = VC_CHARDEV(chr);
2150
    QemuConsole *s = drv->console;
G
Gerd Hoffmann 已提交
2151 2152
    int g_width = 80 * FONT_WIDTH;
    int g_height = 24 * FONT_HEIGHT;
2153

2154 2155
    s->out_fifo.buf = s->out_fifo_buf;
    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
2156
    s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
2157
    s->ds = ds;
2158

B
bellard 已提交
2159 2160 2161 2162 2163
    s->y_displayed = 0;
    s->y_base = 0;
    s->total_height = DEFAULT_BACKSCROLL;
    s->x = 0;
    s->y = 0;
G
Gerd Hoffmann 已提交
2164
    if (!s->surface) {
2165
        if (active_console && active_console->surface) {
G
Gerd Hoffmann 已提交
2166 2167
            g_width = surface_width(active_console->surface);
            g_height = surface_height(active_console->surface);
2168
        }
G
Gerd Hoffmann 已提交
2169
        s->surface = qemu_create_displaysurface(g_width, g_height);
2170
    }
2171

G
Gerd Hoffmann 已提交
2172
    s->hw_ops = &text_console_ops;
B
balrog 已提交
2173 2174
    s->hw = s;

2175 2176 2177 2178 2179 2180
    /* Set text attribute defaults */
    s->t_attrib_default.bold = 0;
    s->t_attrib_default.uline = 0;
    s->t_attrib_default.blink = 0;
    s->t_attrib_default.invers = 0;
    s->t_attrib_default.unvisible = 0;
2181 2182
    s->t_attrib_default.fgcol = QEMU_COLOR_WHITE;
    s->t_attrib_default.bgcol = QEMU_COLOR_BLACK;
2183 2184
    /* set current text attributes to default */
    s->t_attrib = s->t_attrib_default;
B
bellard 已提交
2185 2186
    text_console_resize(s);

2187 2188 2189 2190
    if (chr->label) {
        char msg[128];
        int len;

2191
        s->t_attrib.bgcol = QEMU_COLOR_BLUE;
2192
        len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
2193
        vc_chr_write(chr, (uint8_t *)msg, len);
2194
        s->t_attrib = s->t_attrib_default;
2195 2196
    }

2197
    qemu_chr_be_event(chr, CHR_EVENT_OPENED);
B
bellard 已提交
2198
}
2199

M
Marc-André Lureau 已提交
2200 2201 2202 2203
static void vc_chr_open(Chardev *chr,
                        ChardevBackend *backend,
                        bool *be_opened,
                        Error **errp)
2204
{
2205
    ChardevVC *vc = backend->u.vc.data;
M
Marc-André Lureau 已提交
2206
    VCChardev *drv = VC_CHARDEV(chr);
2207
    QemuConsole *s;
G
Gerd Hoffmann 已提交
2208 2209
    unsigned width = 0;
    unsigned height = 0;
2210

G
Gerd Hoffmann 已提交
2211 2212 2213 2214 2215
    if (vc->has_width) {
        width = vc->width;
    } else if (vc->has_cols) {
        width = vc->cols * FONT_WIDTH;
    }
2216

G
Gerd Hoffmann 已提交
2217 2218 2219 2220 2221
    if (vc->has_height) {
        height = vc->height;
    } else if (vc->has_rows) {
        height = vc->rows * FONT_HEIGHT;
    }
2222

G
Gerd Hoffmann 已提交
2223
    trace_console_txt_new(width, height);
2224
    if (width == 0 || height == 0) {
2225
        s = new_console(NULL, TEXT_CONSOLE, 0);
2226
    } else {
2227
        s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0);
G
Gerd Hoffmann 已提交
2228
        s->surface = qemu_create_displaysurface(width, height);
2229 2230 2231
    }

    if (!s) {
2232
        error_setg(errp, "cannot create text console");
M
Marc-André Lureau 已提交
2233
        return;
2234 2235 2236
    }

    s->chr = chr;
2237
    drv->console = s;
2238 2239 2240 2241

    if (display_state) {
        text_console_do_init(chr, display_state);
    }
2242

2243 2244 2245 2246
    /* console/chardev init sometimes completes elsewhere in a 2nd
     * stage, so defer OPENED events until they are fully initialized
     */
    *be_opened = false;
2247 2248
}

2249
void qemu_console_resize(QemuConsole *s, int width, int height)
2250
{
2251 2252 2253
    DisplaySurface *surface;

    assert(s->console_type == GRAPHIC_CONSOLE);
2254

G
Gerd Hoffmann 已提交
2255
    if (s->surface && (s->surface->flags & QEMU_ALLOCATED_FLAG) &&
2256 2257 2258 2259 2260
        pixman_image_get_width(s->surface->image) == width &&
        pixman_image_get_height(s->surface->image) == height) {
        return;
    }

2261 2262
    surface = qemu_create_displaysurface(width, height);
    dpy_gfx_replace_surface(s, surface);
2263
}
2264

2265 2266
DisplaySurface *qemu_console_surface(QemuConsole *console)
{
2267
    return console->surface;
2268 2269
}

2270 2271
PixelFormat qemu_default_pixelformat(int bpp)
{
2272 2273
    pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
    PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
2274 2275
    return pf;
}
2276

2277 2278 2279 2280 2281 2282 2283 2284
static QemuDisplay *dpys[DISPLAY_TYPE__MAX];

void qemu_display_register(QemuDisplay *ui)
{
    assert(ui->type < DISPLAY_TYPE__MAX);
    dpys[ui->type] = ui;
}

2285 2286 2287 2288 2289 2290 2291 2292 2293 2294
bool qemu_display_find_default(DisplayOptions *opts)
{
    static DisplayType prio[] = {
        DISPLAY_TYPE_GTK,
        DISPLAY_TYPE_SDL,
        DISPLAY_TYPE_COCOA
    };
    int i;

    for (i = 0; i < ARRAY_SIZE(prio); i++) {
2295 2296 2297
        if (dpys[prio[i]] == NULL) {
            ui_module_load_one(DisplayType_lookup.array[prio[i]]);
        }
2298 2299 2300 2301 2302 2303 2304 2305 2306
        if (dpys[prio[i]] == NULL) {
            continue;
        }
        opts->type = prio[i];
        return true;
    }
    return false;
}

2307 2308 2309 2310 2311 2312
void qemu_display_early_init(DisplayOptions *opts)
{
    assert(opts->type < DISPLAY_TYPE__MAX);
    if (opts->type == DISPLAY_TYPE_NONE) {
        return;
    }
2313 2314 2315
    if (dpys[opts->type] == NULL) {
        ui_module_load_one(DisplayType_lookup.array[opts->type]);
    }
2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335
    if (dpys[opts->type] == NULL) {
        error_report("Display '%s' is not available.",
                     DisplayType_lookup.array[opts->type]);
        exit(1);
    }
    if (dpys[opts->type]->early_init) {
        dpys[opts->type]->early_init(opts);
    }
}

void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
{
    assert(opts->type < DISPLAY_TYPE__MAX);
    if (opts->type == DISPLAY_TYPE_NONE) {
        return;
    }
    assert(dpys[opts->type] != NULL);
    dpys[opts->type]->init(ds, opts);
}

2336
void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
G
Gerd Hoffmann 已提交
2337 2338
{
    int val;
2339
    ChardevVC *vc;
G
Gerd Hoffmann 已提交
2340

2341
    backend->type = CHARDEV_BACKEND_KIND_VC;
2342
    vc = backend->u.vc.data = g_new0(ChardevVC, 1);
2343
    qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
G
Gerd Hoffmann 已提交
2344 2345 2346

    val = qemu_opt_get_number(opts, "width", 0);
    if (val != 0) {
2347 2348
        vc->has_width = true;
        vc->width = val;
G
Gerd Hoffmann 已提交
2349 2350 2351 2352
    }

    val = qemu_opt_get_number(opts, "height", 0);
    if (val != 0) {
2353 2354
        vc->has_height = true;
        vc->height = val;
G
Gerd Hoffmann 已提交
2355 2356 2357 2358
    }

    val = qemu_opt_get_number(opts, "cols", 0);
    if (val != 0) {
2359 2360
        vc->has_cols = true;
        vc->cols = val;
G
Gerd Hoffmann 已提交
2361 2362 2363 2364
    }

    val = qemu_opt_get_number(opts, "rows", 0);
    if (val != 0) {
2365 2366
        vc->has_rows = true;
        vc->rows = val;
G
Gerd Hoffmann 已提交
2367 2368 2369
    }
}

G
Gerd Hoffmann 已提交
2370 2371 2372 2373 2374 2375 2376
static const TypeInfo qemu_console_info = {
    .name = TYPE_QEMU_CONSOLE,
    .parent = TYPE_OBJECT,
    .instance_size = sizeof(QemuConsole),
    .class_size = sizeof(QemuConsoleClass),
};

M
Marc-André Lureau 已提交
2377 2378 2379 2380
static void char_vc_class_init(ObjectClass *oc, void *data)
{
    ChardevClass *cc = CHARDEV_CLASS(oc);

M
Marc-André Lureau 已提交
2381
    cc->parse = qemu_chr_parse_vc;
M
Marc-André Lureau 已提交
2382 2383 2384 2385 2386 2387 2388 2389
    cc->open = vc_chr_open;
    cc->chr_write = vc_chr_write;
    cc->chr_set_echo = vc_chr_set_echo;
}

static const TypeInfo char_vc_type_info = {
    .name = TYPE_CHARDEV_VC,
    .parent = TYPE_CHARDEV,
2390
    .instance_size = sizeof(VCChardev),
M
Marc-André Lureau 已提交
2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401
    .class_init = char_vc_class_init,
};

void qemu_console_early_init(void)
{
    /* set the default vc driver */
    if (!object_class_by_name(TYPE_CHARDEV_VC)) {
        type_register(&char_vc_type_info);
    }
}

2402 2403
static void register_types(void)
{
G
Gerd Hoffmann 已提交
2404
    type_register_static(&qemu_console_info);
2405 2406 2407
}

type_init(register_types);