qemu/ui/console.c
<<
>>
Prefs
   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
  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"
  35
  36#define DEFAULT_BACKSCROLL 512
  37#define CONSOLE_CURSOR_PERIOD 500
  38
  39typedef struct TextAttributes {
  40    uint8_t fgcol:4;
  41    uint8_t bgcol:4;
  42    uint8_t bold:1;
  43    uint8_t uline:1;
  44    uint8_t blink:1;
  45    uint8_t invers:1;
  46    uint8_t unvisible:1;
  47} TextAttributes;
  48
  49typedef struct TextCell {
  50    uint8_t ch;
  51    TextAttributes t_attrib;
  52} TextCell;
  53
  54#define MAX_ESC_PARAMS 3
  55
  56enum TTYState {
  57    TTY_STATE_NORM,
  58    TTY_STATE_ESC,
  59    TTY_STATE_CSI,
  60};
  61
  62typedef struct QEMUFIFO {
  63    uint8_t *buf;
  64    int buf_size;
  65    int count, wptr, rptr;
  66} QEMUFIFO;
  67
  68static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
  69{
  70    int l, len;
  71
  72    l = f->buf_size - f->count;
  73    if (len1 > l)
  74        len1 = l;
  75    len = len1;
  76    while (len > 0) {
  77        l = f->buf_size - f->wptr;
  78        if (l > len)
  79            l = len;
  80        memcpy(f->buf + f->wptr, buf, l);
  81        f->wptr += l;
  82        if (f->wptr >= f->buf_size)
  83            f->wptr = 0;
  84        buf += l;
  85        len -= l;
  86    }
  87    f->count += len1;
  88    return len1;
  89}
  90
  91static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
  92{
  93    int l, len;
  94
  95    if (len1 > f->count)
  96        len1 = f->count;
  97    len = len1;
  98    while (len > 0) {
  99        l = f->buf_size - f->rptr;
 100        if (l > len)
 101            l = len;
 102        memcpy(buf, f->buf + f->rptr, l);
 103        f->rptr += l;
 104        if (f->rptr >= f->buf_size)
 105            f->rptr = 0;
 106        buf += l;
 107        len -= l;
 108    }
 109    f->count -= len1;
 110    return len1;
 111}
 112
 113typedef enum {
 114    GRAPHIC_CONSOLE,
 115    TEXT_CONSOLE,
 116    TEXT_CONSOLE_FIXED_SIZE
 117} console_type_t;
 118
 119struct QemuConsole {
 120    Object parent;
 121
 122    int index;
 123    console_type_t console_type;
 124    DisplayState *ds;
 125    DisplaySurface *surface;
 126    int dcls;
 127    DisplayChangeListener *gl;
 128    bool gl_block;
 129    int window_id;
 130
 131    /* Graphic console state.  */
 132    Object *device;
 133    uint32_t head;
 134    QemuUIInfo ui_info;
 135    QEMUTimer *ui_timer;
 136    const GraphicHwOps *hw_ops;
 137    void *hw;
 138
 139    /* Text console state */
 140    int width;
 141    int height;
 142    int total_height;
 143    int backscroll_height;
 144    int x, y;
 145    int x_saved, y_saved;
 146    int y_displayed;
 147    int y_base;
 148    TextAttributes t_attrib_default; /* default text attributes */
 149    TextAttributes t_attrib; /* currently active text attributes */
 150    TextCell *cells;
 151    int text_x[2], text_y[2], cursor_invalidate;
 152    int echo;
 153
 154    int update_x0;
 155    int update_y0;
 156    int update_x1;
 157    int update_y1;
 158
 159    enum TTYState state;
 160    int esc_params[MAX_ESC_PARAMS];
 161    int nb_esc_params;
 162
 163    Chardev *chr;
 164    /* fifo for key pressed */
 165    QEMUFIFO out_fifo;
 166    uint8_t out_fifo_buf[16];
 167    QEMUTimer *kbd_timer;
 168};
 169
 170struct DisplayState {
 171    QEMUTimer *gui_timer;
 172    uint64_t last_update;
 173    uint64_t update_interval;
 174    bool refreshing;
 175    bool have_gfx;
 176    bool have_text;
 177
 178    QLIST_HEAD(, DisplayChangeListener) listeners;
 179};
 180
 181static DisplayState *display_state;
 182static QemuConsole *active_console;
 183static QemuConsole **consoles;
 184static int nb_consoles = 0;
 185static bool cursor_visible_phase;
 186static QEMUTimer *cursor_timer;
 187
 188static void text_console_do_init(Chardev *chr, DisplayState *ds);
 189static void dpy_refresh(DisplayState *s);
 190static DisplayState *get_alloc_displaystate(void);
 191static void text_console_update_cursor_timer(void);
 192static void text_console_update_cursor(void *opaque);
 193
 194static void gui_update(void *opaque)
 195{
 196    uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
 197    uint64_t dcl_interval;
 198    DisplayState *ds = opaque;
 199    DisplayChangeListener *dcl;
 200    int i;
 201
 202    ds->refreshing = true;
 203    dpy_refresh(ds);
 204    ds->refreshing = false;
 205
 206    QLIST_FOREACH(dcl, &ds->listeners, next) {
 207        dcl_interval = dcl->update_interval ?
 208            dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
 209        if (interval > dcl_interval) {
 210            interval = dcl_interval;
 211        }
 212    }
 213    if (ds->update_interval != interval) {
 214        ds->update_interval = interval;
 215        for (i = 0; i < nb_consoles; i++) {
 216            if (consoles[i]->hw_ops->update_interval) {
 217                consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval);
 218            }
 219        }
 220        trace_console_refresh(interval);
 221    }
 222    ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
 223    timer_mod(ds->gui_timer, ds->last_update + interval);
 224}
 225
 226static void gui_setup_refresh(DisplayState *ds)
 227{
 228    DisplayChangeListener *dcl;
 229    bool need_timer = false;
 230    bool have_gfx = false;
 231    bool have_text = false;
 232
 233    QLIST_FOREACH(dcl, &ds->listeners, next) {
 234        if (dcl->ops->dpy_refresh != NULL) {
 235            need_timer = true;
 236        }
 237        if (dcl->ops->dpy_gfx_update != NULL) {
 238            have_gfx = true;
 239        }
 240        if (dcl->ops->dpy_text_update != NULL) {
 241            have_text = true;
 242        }
 243    }
 244
 245    if (need_timer && ds->gui_timer == NULL) {
 246        ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
 247        timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
 248    }
 249    if (!need_timer && ds->gui_timer != NULL) {
 250        timer_del(ds->gui_timer);
 251        timer_free(ds->gui_timer);
 252        ds->gui_timer = NULL;
 253    }
 254
 255    ds->have_gfx = have_gfx;
 256    ds->have_text = have_text;
 257}
 258
 259void graphic_hw_update(QemuConsole *con)
 260{
 261    if (!con) {
 262        con = active_console;
 263    }
 264    if (con && con->hw_ops->gfx_update) {
 265        con->hw_ops->gfx_update(con->hw);
 266    }
 267}
 268
 269void graphic_hw_gl_block(QemuConsole *con, bool block)
 270{
 271    assert(con != NULL);
 272
 273    con->gl_block = block;
 274    if (con->hw_ops->gl_block) {
 275        con->hw_ops->gl_block(con->hw, block);
 276    }
 277}
 278
 279int qemu_console_get_window_id(QemuConsole *con)
 280{
 281    return con->window_id;
 282}
 283
 284void qemu_console_set_window_id(QemuConsole *con, int window_id)
 285{
 286    con->window_id = window_id;
 287}
 288
 289void graphic_hw_invalidate(QemuConsole *con)
 290{
 291    if (!con) {
 292        con = active_console;
 293    }
 294    if (con && con->hw_ops->invalidate) {
 295        con->hw_ops->invalidate(con->hw);
 296    }
 297}
 298
 299static void ppm_save(const char *filename, DisplaySurface *ds,
 300                     Error **errp)
 301{
 302    int width = pixman_image_get_width(ds->image);
 303    int height = pixman_image_get_height(ds->image);
 304    int fd;
 305    FILE *f;
 306    int y;
 307    int ret;
 308    pixman_image_t *linebuf;
 309
 310    trace_ppm_save(filename, ds);
 311    fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
 312    if (fd == -1) {
 313        error_setg(errp, "failed to open file '%s': %s", filename,
 314                   strerror(errno));
 315        return;
 316    }
 317    f = fdopen(fd, "wb");
 318    ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
 319    if (ret < 0) {
 320        linebuf = NULL;
 321        goto write_err;
 322    }
 323    linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
 324    for (y = 0; y < height; y++) {
 325        qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
 326        clearerr(f);
 327        ret = fwrite(pixman_image_get_data(linebuf), 1,
 328                     pixman_image_get_stride(linebuf), f);
 329        (void)ret;
 330        if (ferror(f)) {
 331            goto write_err;
 332        }
 333    }
 334
 335out:
 336    qemu_pixman_image_unref(linebuf);
 337    fclose(f);
 338    return;
 339
 340write_err:
 341    error_setg(errp, "failed to write to file '%s': %s", filename,
 342               strerror(errno));
 343    unlink(filename);
 344    goto out;
 345}
 346
 347void qmp_screendump(const char *filename, bool has_device, const char *device,
 348                    bool has_head, int64_t head, Error **errp)
 349{
 350    QemuConsole *con;
 351    DisplaySurface *surface;
 352
 353    if (has_device) {
 354        con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
 355                                                 errp);
 356        if (!con) {
 357            return;
 358        }
 359    } else {
 360        if (has_head) {
 361            error_setg(errp, "'head' must be specified together with 'device'");
 362            return;
 363        }
 364        con = qemu_console_lookup_by_index(0);
 365        if (!con) {
 366            error_setg(errp, "There is no console to take a screendump from");
 367            return;
 368        }
 369    }
 370
 371    graphic_hw_update(con);
 372    surface = qemu_console_surface(con);
 373    if (!surface) {
 374        error_setg(errp, "no surface");
 375        return;
 376    }
 377
 378    ppm_save(filename, surface, errp);
 379}
 380
 381void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
 382{
 383    if (!con) {
 384        con = active_console;
 385    }
 386    if (con && con->hw_ops->text_update) {
 387        con->hw_ops->text_update(con->hw, chardata);
 388    }
 389}
 390
 391static void vga_fill_rect(QemuConsole *con,
 392                          int posx, int posy, int width, int height,
 393                          pixman_color_t color)
 394{
 395    DisplaySurface *surface = qemu_console_surface(con);
 396    pixman_rectangle16_t rect = {
 397        .x = posx, .y = posy, .width = width, .height = height
 398    };
 399
 400    pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
 401                                 &color, 1, &rect);
 402}
 403
 404/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
 405static void vga_bitblt(QemuConsole *con,
 406                       int xs, int ys, int xd, int yd, int w, int h)
 407{
 408    DisplaySurface *surface = qemu_console_surface(con);
 409
 410    pixman_image_composite(PIXMAN_OP_SRC,
 411                           surface->image, NULL, surface->image,
 412                           xs, ys, 0, 0, xd, yd, w, h);
 413}
 414
 415/***********************************************************/
 416/* basic char display */
 417
 418#define FONT_HEIGHT 16
 419#define FONT_WIDTH 8
 420
 421#include "vgafont.h"
 422
 423#define QEMU_RGB(r, g, b)                                               \
 424    { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
 425
 426static const pixman_color_t color_table_rgb[2][8] = {
 427    {   /* dark */
 428        [QEMU_COLOR_BLACK]   = QEMU_RGB(0x00, 0x00, 0x00),  /* black */
 429        [QEMU_COLOR_BLUE]    = QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
 430        [QEMU_COLOR_GREEN]   = QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
 431        [QEMU_COLOR_CYAN]    = QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
 432        [QEMU_COLOR_RED]     = QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
 433        [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
 434        [QEMU_COLOR_YELLOW]  = QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
 435        [QEMU_COLOR_WHITE]   = QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
 436    },
 437    {   /* bright */
 438        [QEMU_COLOR_BLACK]   = QEMU_RGB(0x00, 0x00, 0x00),  /* black */
 439        [QEMU_COLOR_BLUE]    = QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
 440        [QEMU_COLOR_GREEN]   = QEMU_RGB(0x00, 0xff, 0x00),  /* green */
 441        [QEMU_COLOR_CYAN]    = QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
 442        [QEMU_COLOR_RED]     = QEMU_RGB(0xff, 0x00, 0x00),  /* red */
 443        [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
 444        [QEMU_COLOR_YELLOW]  = QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
 445        [QEMU_COLOR_WHITE]   = QEMU_RGB(0xff, 0xff, 0xff),  /* white */
 446    }
 447};
 448
 449static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
 450                          TextAttributes *t_attrib)
 451{
 452    static pixman_image_t *glyphs[256];
 453    DisplaySurface *surface = qemu_console_surface(s);
 454    pixman_color_t fgcol, bgcol;
 455
 456    if (t_attrib->invers) {
 457        bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
 458        fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
 459    } else {
 460        fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
 461        bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
 462    }
 463
 464    if (!glyphs[ch]) {
 465        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
 466    }
 467    qemu_pixman_glyph_render(glyphs[ch], surface->image,
 468                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
 469}
 470
 471static void text_console_resize(QemuConsole *s)
 472{
 473    TextCell *cells, *c, *c1;
 474    int w1, x, y, last_width;
 475
 476    last_width = s->width;
 477    s->width = surface_width(s->surface) / FONT_WIDTH;
 478    s->height = surface_height(s->surface) / FONT_HEIGHT;
 479
 480    w1 = last_width;
 481    if (s->width < w1)
 482        w1 = s->width;
 483
 484    cells = g_new(TextCell, s->width * s->total_height);
 485    for(y = 0; y < s->total_height; y++) {
 486        c = &cells[y * s->width];
 487        if (w1 > 0) {
 488            c1 = &s->cells[y * last_width];
 489            for(x = 0; x < w1; x++) {
 490                *c++ = *c1++;
 491            }
 492        }
 493        for(x = w1; x < s->width; x++) {
 494            c->ch = ' ';
 495            c->t_attrib = s->t_attrib_default;
 496            c++;
 497        }
 498    }
 499    g_free(s->cells);
 500    s->cells = cells;
 501}
 502
 503static inline void text_update_xy(QemuConsole *s, int x, int y)
 504{
 505    s->text_x[0] = MIN(s->text_x[0], x);
 506    s->text_x[1] = MAX(s->text_x[1], x);
 507    s->text_y[0] = MIN(s->text_y[0], y);
 508    s->text_y[1] = MAX(s->text_y[1], y);
 509}
 510
 511static void invalidate_xy(QemuConsole *s, int x, int y)
 512{
 513    if (!qemu_console_is_visible(s)) {
 514        return;
 515    }
 516    if (s->update_x0 > x * FONT_WIDTH)
 517        s->update_x0 = x * FONT_WIDTH;
 518    if (s->update_y0 > y * FONT_HEIGHT)
 519        s->update_y0 = y * FONT_HEIGHT;
 520    if (s->update_x1 < (x + 1) * FONT_WIDTH)
 521        s->update_x1 = (x + 1) * FONT_WIDTH;
 522    if (s->update_y1 < (y + 1) * FONT_HEIGHT)
 523        s->update_y1 = (y + 1) * FONT_HEIGHT;
 524}
 525
 526static void update_xy(QemuConsole *s, int x, int y)
 527{
 528    TextCell *c;
 529    int y1, y2;
 530
 531    if (s->ds->have_text) {
 532        text_update_xy(s, x, y);
 533    }
 534
 535    y1 = (s->y_base + y) % s->total_height;
 536    y2 = y1 - s->y_displayed;
 537    if (y2 < 0) {
 538        y2 += s->total_height;
 539    }
 540    if (y2 < s->height) {
 541        c = &s->cells[y1 * s->width + x];
 542        vga_putcharxy(s, x, y2, c->ch,
 543                      &(c->t_attrib));
 544        invalidate_xy(s, x, y2);
 545    }
 546}
 547
 548static void console_show_cursor(QemuConsole *s, int show)
 549{
 550    TextCell *c;
 551    int y, y1;
 552    int x = s->x;
 553
 554    if (s->ds->have_text) {
 555        s->cursor_invalidate = 1;
 556    }
 557
 558    if (x >= s->width) {
 559        x = s->width - 1;
 560    }
 561    y1 = (s->y_base + s->y) % s->total_height;
 562    y = y1 - s->y_displayed;
 563    if (y < 0) {
 564        y += s->total_height;
 565    }
 566    if (y < s->height) {
 567        c = &s->cells[y1 * s->width + x];
 568        if (show && cursor_visible_phase) {
 569            TextAttributes t_attrib = s->t_attrib_default;
 570            t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
 571            vga_putcharxy(s, x, y, c->ch, &t_attrib);
 572        } else {
 573            vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
 574        }
 575        invalidate_xy(s, x, y);
 576    }
 577}
 578
 579static void console_refresh(QemuConsole *s)
 580{
 581    DisplaySurface *surface = qemu_console_surface(s);
 582    TextCell *c;
 583    int x, y, y1;
 584
 585    if (s->ds->have_text) {
 586        s->text_x[0] = 0;
 587        s->text_y[0] = 0;
 588        s->text_x[1] = s->width - 1;
 589        s->text_y[1] = s->height - 1;
 590        s->cursor_invalidate = 1;
 591    }
 592
 593    vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
 594                  color_table_rgb[0][QEMU_COLOR_BLACK]);
 595    y1 = s->y_displayed;
 596    for (y = 0; y < s->height; y++) {
 597        c = s->cells + y1 * s->width;
 598        for (x = 0; x < s->width; x++) {
 599            vga_putcharxy(s, x, y, c->ch,
 600                          &(c->t_attrib));
 601            c++;
 602        }
 603        if (++y1 == s->total_height) {
 604            y1 = 0;
 605        }
 606    }
 607    console_show_cursor(s, 1);
 608    dpy_gfx_update(s, 0, 0,
 609                   surface_width(surface), surface_height(surface));
 610}
 611
 612static void console_scroll(QemuConsole *s, int ydelta)
 613{
 614    int i, y1;
 615
 616    if (ydelta > 0) {
 617        for(i = 0; i < ydelta; i++) {
 618            if (s->y_displayed == s->y_base)
 619                break;
 620            if (++s->y_displayed == s->total_height)
 621                s->y_displayed = 0;
 622        }
 623    } else {
 624        ydelta = -ydelta;
 625        i = s->backscroll_height;
 626        if (i > s->total_height - s->height)
 627            i = s->total_height - s->height;
 628        y1 = s->y_base - i;
 629        if (y1 < 0)
 630            y1 += s->total_height;
 631        for(i = 0; i < ydelta; i++) {
 632            if (s->y_displayed == y1)
 633                break;
 634            if (--s->y_displayed < 0)
 635                s->y_displayed = s->total_height - 1;
 636        }
 637    }
 638    console_refresh(s);
 639}
 640
 641static void console_put_lf(QemuConsole *s)
 642{
 643    TextCell *c;
 644    int x, y1;
 645
 646    s->y++;
 647    if (s->y >= s->height) {
 648        s->y = s->height - 1;
 649
 650        if (s->y_displayed == s->y_base) {
 651            if (++s->y_displayed == s->total_height)
 652                s->y_displayed = 0;
 653        }
 654        if (++s->y_base == s->total_height)
 655            s->y_base = 0;
 656        if (s->backscroll_height < s->total_height)
 657            s->backscroll_height++;
 658        y1 = (s->y_base + s->height - 1) % s->total_height;
 659        c = &s->cells[y1 * s->width];
 660        for(x = 0; x < s->width; x++) {
 661            c->ch = ' ';
 662            c->t_attrib = s->t_attrib_default;
 663            c++;
 664        }
 665        if (s->y_displayed == s->y_base) {
 666            if (s->ds->have_text) {
 667                s->text_x[0] = 0;
 668                s->text_y[0] = 0;
 669                s->text_x[1] = s->width - 1;
 670                s->text_y[1] = s->height - 1;
 671            }
 672
 673            vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
 674                       s->width * FONT_WIDTH,
 675                       (s->height - 1) * FONT_HEIGHT);
 676            vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
 677                          s->width * FONT_WIDTH, FONT_HEIGHT,
 678                          color_table_rgb[0][s->t_attrib_default.bgcol]);
 679            s->update_x0 = 0;
 680            s->update_y0 = 0;
 681            s->update_x1 = s->width * FONT_WIDTH;
 682            s->update_y1 = s->height * FONT_HEIGHT;
 683        }
 684    }
 685}
 686
 687/* Set console attributes depending on the current escape codes.
 688 * NOTE: I know this code is not very efficient (checking every color for it
 689 * self) but it is more readable and better maintainable.
 690 */
 691static void console_handle_escape(QemuConsole *s)
 692{
 693    int i;
 694
 695    for (i=0; i<s->nb_esc_params; i++) {
 696        switch (s->esc_params[i]) {
 697            case 0: /* reset all console attributes to default */
 698                s->t_attrib = s->t_attrib_default;
 699                break;
 700            case 1:
 701                s->t_attrib.bold = 1;
 702                break;
 703            case 4:
 704                s->t_attrib.uline = 1;
 705                break;
 706            case 5:
 707                s->t_attrib.blink = 1;
 708                break;
 709            case 7:
 710                s->t_attrib.invers = 1;
 711                break;
 712            case 8:
 713                s->t_attrib.unvisible = 1;
 714                break;
 715            case 22:
 716                s->t_attrib.bold = 0;
 717                break;
 718            case 24:
 719                s->t_attrib.uline = 0;
 720                break;
 721            case 25:
 722                s->t_attrib.blink = 0;
 723                break;
 724            case 27:
 725                s->t_attrib.invers = 0;
 726                break;
 727            case 28:
 728                s->t_attrib.unvisible = 0;
 729                break;
 730            /* set foreground color */
 731            case 30:
 732                s->t_attrib.fgcol = QEMU_COLOR_BLACK;
 733                break;
 734            case 31:
 735                s->t_attrib.fgcol = QEMU_COLOR_RED;
 736                break;
 737            case 32:
 738                s->t_attrib.fgcol = QEMU_COLOR_GREEN;
 739                break;
 740            case 33:
 741                s->t_attrib.fgcol = QEMU_COLOR_YELLOW;
 742                break;
 743            case 34:
 744                s->t_attrib.fgcol = QEMU_COLOR_BLUE;
 745                break;
 746            case 35:
 747                s->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
 748                break;
 749            case 36:
 750                s->t_attrib.fgcol = QEMU_COLOR_CYAN;
 751                break;
 752            case 37:
 753                s->t_attrib.fgcol = QEMU_COLOR_WHITE;
 754                break;
 755            /* set background color */
 756            case 40:
 757                s->t_attrib.bgcol = QEMU_COLOR_BLACK;
 758                break;
 759            case 41:
 760                s->t_attrib.bgcol = QEMU_COLOR_RED;
 761                break;
 762            case 42:
 763                s->t_attrib.bgcol = QEMU_COLOR_GREEN;
 764                break;
 765            case 43:
 766                s->t_attrib.bgcol = QEMU_COLOR_YELLOW;
 767                break;
 768            case 44:
 769                s->t_attrib.bgcol = QEMU_COLOR_BLUE;
 770                break;
 771            case 45:
 772                s->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
 773                break;
 774            case 46:
 775                s->t_attrib.bgcol = QEMU_COLOR_CYAN;
 776                break;
 777            case 47:
 778                s->t_attrib.bgcol = QEMU_COLOR_WHITE;
 779                break;
 780        }
 781    }
 782}
 783
 784static void console_clear_xy(QemuConsole *s, int x, int y)
 785{
 786    int y1 = (s->y_base + y) % s->total_height;
 787    TextCell *c = &s->cells[y1 * s->width + x];
 788    c->ch = ' ';
 789    c->t_attrib = s->t_attrib_default;
 790    update_xy(s, x, y);
 791}
 792
 793static void console_put_one(QemuConsole *s, int ch)
 794{
 795    TextCell *c;
 796    int y1;
 797    if (s->x >= s->width) {
 798        /* line wrap */
 799        s->x = 0;
 800        console_put_lf(s);
 801    }
 802    y1 = (s->y_base + s->y) % s->total_height;
 803    c = &s->cells[y1 * s->width + s->x];
 804    c->ch = ch;
 805    c->t_attrib = s->t_attrib;
 806    update_xy(s, s->x, s->y);
 807    s->x++;
 808}
 809
 810static void console_respond_str(QemuConsole *s, const char *buf)
 811{
 812    while (*buf) {
 813        console_put_one(s, *buf);
 814        buf++;
 815    }
 816}
 817
 818/* set cursor, checking bounds */
 819static void set_cursor(QemuConsole *s, int x, int y)
 820{
 821    if (x < 0) {
 822        x = 0;
 823    }
 824    if (y < 0) {
 825        y = 0;
 826    }
 827    if (y >= s->height) {
 828        y = s->height - 1;
 829    }
 830    if (x >= s->width) {
 831        x = s->width - 1;
 832    }
 833
 834    s->x = x;
 835    s->y = y;
 836}
 837
 838static void console_putchar(QemuConsole *s, int ch)
 839{
 840    int i;
 841    int x, y;
 842    char response[40];
 843
 844    switch(s->state) {
 845    case TTY_STATE_NORM:
 846        switch(ch) {
 847        case '\r':  /* carriage return */
 848            s->x = 0;
 849            break;
 850        case '\n':  /* newline */
 851            console_put_lf(s);
 852            break;
 853        case '\b':  /* backspace */
 854            if (s->x > 0)
 855                s->x--;
 856            break;
 857        case '\t':  /* tabspace */
 858            if (s->x + (8 - (s->x % 8)) > s->width) {
 859                s->x = 0;
 860                console_put_lf(s);
 861            } else {
 862                s->x = s->x + (8 - (s->x % 8));
 863            }
 864            break;
 865        case '\a':  /* alert aka. bell */
 866            /* TODO: has to be implemented */
 867            break;
 868        case 14:
 869            /* SI (shift in), character set 0 (ignored) */
 870            break;
 871        case 15:
 872            /* SO (shift out), character set 1 (ignored) */
 873            break;
 874        case 27:    /* esc (introducing an escape sequence) */
 875            s->state = TTY_STATE_ESC;
 876            break;
 877        default:
 878            console_put_one(s, ch);
 879            break;
 880        }
 881        break;
 882    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
 883        if (ch == '[') {
 884            for(i=0;i<MAX_ESC_PARAMS;i++)
 885                s->esc_params[i] = 0;
 886            s->nb_esc_params = 0;
 887            s->state = TTY_STATE_CSI;
 888        } else {
 889            s->state = TTY_STATE_NORM;
 890        }
 891        break;
 892    case TTY_STATE_CSI: /* handle escape sequence parameters */
 893        if (ch >= '0' && ch <= '9') {
 894            if (s->nb_esc_params < MAX_ESC_PARAMS) {
 895                int *param = &s->esc_params[s->nb_esc_params];
 896                int digit = (ch - '0');
 897
 898                *param = (*param <= (INT_MAX - digit) / 10) ?
 899                         *param * 10 + digit : INT_MAX;
 900            }
 901        } else {
 902            if (s->nb_esc_params < MAX_ESC_PARAMS)
 903                s->nb_esc_params++;
 904            if (ch == ';' || ch == '?') {
 905                break;
 906            }
 907            trace_console_putchar_csi(s->esc_params[0], s->esc_params[1],
 908                                      ch, s->nb_esc_params);
 909            s->state = TTY_STATE_NORM;
 910            switch(ch) {
 911            case 'A':
 912                /* move cursor up */
 913                if (s->esc_params[0] == 0) {
 914                    s->esc_params[0] = 1;
 915                }
 916                set_cursor(s, s->x, s->y - s->esc_params[0]);
 917                break;
 918            case 'B':
 919                /* move cursor down */
 920                if (s->esc_params[0] == 0) {
 921                    s->esc_params[0] = 1;
 922                }
 923                set_cursor(s, s->x, s->y + s->esc_params[0]);
 924                break;
 925            case 'C':
 926                /* move cursor right */
 927                if (s->esc_params[0] == 0) {
 928                    s->esc_params[0] = 1;
 929                }
 930                set_cursor(s, s->x + s->esc_params[0], s->y);
 931                break;
 932            case 'D':
 933                /* move cursor left */
 934                if (s->esc_params[0] == 0) {
 935                    s->esc_params[0] = 1;
 936                }
 937                set_cursor(s, s->x - s->esc_params[0], s->y);
 938                break;
 939            case 'G':
 940                /* move cursor to column */
 941                set_cursor(s, s->esc_params[0] - 1, s->y);
 942                break;
 943            case 'f':
 944            case 'H':
 945                /* move cursor to row, column */
 946                set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
 947                break;
 948            case 'J':
 949                switch (s->esc_params[0]) {
 950                case 0:
 951                    /* clear to end of screen */
 952                    for (y = s->y; y < s->height; y++) {
 953                        for (x = 0; x < s->width; x++) {
 954                            if (y == s->y && x < s->x) {
 955                                continue;
 956                            }
 957                            console_clear_xy(s, x, y);
 958                        }
 959                    }
 960                    break;
 961                case 1:
 962                    /* clear from beginning of screen */
 963                    for (y = 0; y <= s->y; y++) {
 964                        for (x = 0; x < s->width; x++) {
 965                            if (y == s->y && x > s->x) {
 966                                break;
 967                            }
 968                            console_clear_xy(s, x, y);
 969                        }
 970                    }
 971                    break;
 972                case 2:
 973                    /* clear entire screen */
 974                    for (y = 0; y <= s->height; y++) {
 975                        for (x = 0; x < s->width; x++) {
 976                            console_clear_xy(s, x, y);
 977                        }
 978                    }
 979                    break;
 980                }
 981                break;
 982            case 'K':
 983                switch (s->esc_params[0]) {
 984                case 0:
 985                    /* clear to eol */
 986                    for(x = s->x; x < s->width; x++) {
 987                        console_clear_xy(s, x, s->y);
 988                    }
 989                    break;
 990                case 1:
 991                    /* clear from beginning of line */
 992                    for (x = 0; x <= s->x; x++) {
 993                        console_clear_xy(s, x, s->y);
 994                    }
 995                    break;
 996                case 2:
 997                    /* clear entire line */
 998                    for(x = 0; x < s->width; x++) {
 999                        console_clear_xy(s, x, s->y);
1000                    }
1001                    break;
1002                }
1003                break;
1004            case 'm':
1005                console_handle_escape(s);
1006                break;
1007            case 'n':
1008                switch (s->esc_params[0]) {
1009                case 5:
1010                    /* report console status (always succeed)*/
1011                    console_respond_str(s, "\033[0n");
1012                    break;
1013                case 6:
1014                    /* report cursor position */
1015                    sprintf(response, "\033[%d;%dR",
1016                           (s->y_base + s->y) % s->total_height + 1,
1017                            s->x + 1);
1018                    console_respond_str(s, response);
1019                    break;
1020                }
1021                break;
1022            case 's':
1023                /* save cursor position */
1024                s->x_saved = s->x;
1025                s->y_saved = s->y;
1026                break;
1027            case 'u':
1028                /* restore cursor position */
1029                s->x = s->x_saved;
1030                s->y = s->y_saved;
1031                break;
1032            default:
1033                trace_console_putchar_unhandled(ch);
1034                break;
1035            }
1036            break;
1037        }
1038    }
1039}
1040
1041void console_select(unsigned int index)
1042{
1043    DisplayChangeListener *dcl;
1044    QemuConsole *s;
1045
1046    trace_console_select(index);
1047    s = qemu_console_lookup_by_index(index);
1048    if (s) {
1049        DisplayState *ds = s->ds;
1050
1051        active_console = s;
1052        if (ds->have_gfx) {
1053            QLIST_FOREACH(dcl, &ds->listeners, next) {
1054                if (dcl->con != NULL) {
1055                    continue;
1056                }
1057                if (dcl->ops->dpy_gfx_switch) {
1058                    dcl->ops->dpy_gfx_switch(dcl, s->surface);
1059                }
1060            }
1061            if (s->surface) {
1062                dpy_gfx_update(s, 0, 0, surface_width(s->surface),
1063                               surface_height(s->surface));
1064            }
1065        }
1066        if (ds->have_text) {
1067            dpy_text_resize(s, s->width, s->height);
1068        }
1069        text_console_update_cursor(NULL);
1070    }
1071}
1072
1073typedef struct VCChardev {
1074    Chardev parent;
1075    QemuConsole *console;
1076} VCChardev;
1077
1078#define TYPE_CHARDEV_VC "chardev-vc"
1079#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
1080
1081static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1082{
1083    VCChardev *drv = VC_CHARDEV(chr);
1084    QemuConsole *s = drv->console;
1085    int i;
1086
1087    if (!s->ds) {
1088        return 0;
1089    }
1090
1091    s->update_x0 = s->width * FONT_WIDTH;
1092    s->update_y0 = s->height * FONT_HEIGHT;
1093    s->update_x1 = 0;
1094    s->update_y1 = 0;
1095    console_show_cursor(s, 0);
1096    for(i = 0; i < len; i++) {
1097        console_putchar(s, buf[i]);
1098    }
1099    console_show_cursor(s, 1);
1100    if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
1101        dpy_gfx_update(s, s->update_x0, s->update_y0,
1102                       s->update_x1 - s->update_x0,
1103                       s->update_y1 - s->update_y0);
1104    }
1105    return len;
1106}
1107
1108static void kbd_send_chars(void *opaque)
1109{
1110    QemuConsole *s = opaque;
1111    int len;
1112    uint8_t buf[16];
1113
1114    len = qemu_chr_be_can_write(s->chr);
1115    if (len > s->out_fifo.count)
1116        len = s->out_fifo.count;
1117    if (len > 0) {
1118        if (len > sizeof(buf))
1119            len = sizeof(buf);
1120        qemu_fifo_read(&s->out_fifo, buf, len);
1121        qemu_chr_be_write(s->chr, buf, len);
1122    }
1123    /* characters are pending: we send them a bit later (XXX:
1124       horrible, should change char device API) */
1125    if (s->out_fifo.count > 0) {
1126        timer_mod(s->kbd_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1);
1127    }
1128}
1129
1130/* called when an ascii key is pressed */
1131void kbd_put_keysym_console(QemuConsole *s, int keysym)
1132{
1133    uint8_t buf[16], *q;
1134    CharBackend *be;
1135    int c;
1136
1137    if (!s || (s->console_type == GRAPHIC_CONSOLE))
1138        return;
1139
1140    switch(keysym) {
1141    case QEMU_KEY_CTRL_UP:
1142        console_scroll(s, -1);
1143        break;
1144    case QEMU_KEY_CTRL_DOWN:
1145        console_scroll(s, 1);
1146        break;
1147    case QEMU_KEY_CTRL_PAGEUP:
1148        console_scroll(s, -10);
1149        break;
1150    case QEMU_KEY_CTRL_PAGEDOWN:
1151        console_scroll(s, 10);
1152        break;
1153    default:
1154        /* convert the QEMU keysym to VT100 key string */
1155        q = buf;
1156        if (keysym >= 0xe100 && keysym <= 0xe11f) {
1157            *q++ = '\033';
1158            *q++ = '[';
1159            c = keysym - 0xe100;
1160            if (c >= 10)
1161                *q++ = '0' + (c / 10);
1162            *q++ = '0' + (c % 10);
1163            *q++ = '~';
1164        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1165            *q++ = '\033';
1166            *q++ = '[';
1167            *q++ = keysym & 0xff;
1168        } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1169            vc_chr_write(s->chr, (const uint8_t *) "\r", 1);
1170            *q++ = '\n';
1171        } else {
1172            *q++ = keysym;
1173        }
1174        if (s->echo) {
1175            vc_chr_write(s->chr, buf, q - buf);
1176        }
1177        be = s->chr->be;
1178        if (be && be->chr_read) {
1179            qemu_fifo_write(&s->out_fifo, buf, q - buf);
1180            kbd_send_chars(s);
1181        }
1182        break;
1183    }
1184}
1185
1186static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
1187    [Q_KEY_CODE_UP]     = QEMU_KEY_UP,
1188    [Q_KEY_CODE_DOWN]   = QEMU_KEY_DOWN,
1189    [Q_KEY_CODE_RIGHT]  = QEMU_KEY_RIGHT,
1190    [Q_KEY_CODE_LEFT]   = QEMU_KEY_LEFT,
1191    [Q_KEY_CODE_HOME]   = QEMU_KEY_HOME,
1192    [Q_KEY_CODE_END]    = QEMU_KEY_END,
1193    [Q_KEY_CODE_PGUP]   = QEMU_KEY_PAGEUP,
1194    [Q_KEY_CODE_PGDN]   = QEMU_KEY_PAGEDOWN,
1195    [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
1196    [Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
1197};
1198
1199static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
1200    [Q_KEY_CODE_UP]     = QEMU_KEY_CTRL_UP,
1201    [Q_KEY_CODE_DOWN]   = QEMU_KEY_CTRL_DOWN,
1202    [Q_KEY_CODE_RIGHT]  = QEMU_KEY_CTRL_RIGHT,
1203    [Q_KEY_CODE_LEFT]   = QEMU_KEY_CTRL_LEFT,
1204    [Q_KEY_CODE_HOME]   = QEMU_KEY_CTRL_HOME,
1205    [Q_KEY_CODE_END]    = QEMU_KEY_CTRL_END,
1206    [Q_KEY_CODE_PGUP]   = QEMU_KEY_CTRL_PAGEUP,
1207    [Q_KEY_CODE_PGDN]   = QEMU_KEY_CTRL_PAGEDOWN,
1208};
1209
1210bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
1211{
1212    int keysym;
1213
1214    keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
1215    if (keysym == 0) {
1216        return false;
1217    }
1218    kbd_put_keysym_console(s, keysym);
1219    return true;
1220}
1221
1222void kbd_put_string_console(QemuConsole *s, const char *str, int len)
1223{
1224    int i;
1225
1226    for (i = 0; i < len && str[i]; i++) {
1227        kbd_put_keysym_console(s, str[i]);
1228    }
1229}
1230
1231void kbd_put_keysym(int keysym)
1232{
1233    kbd_put_keysym_console(active_console, keysym);
1234}
1235
1236static void text_console_invalidate(void *opaque)
1237{
1238    QemuConsole *s = (QemuConsole *) opaque;
1239
1240    if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
1241        text_console_resize(s);
1242    }
1243    console_refresh(s);
1244}
1245
1246static void text_console_update(void *opaque, console_ch_t *chardata)
1247{
1248    QemuConsole *s = (QemuConsole *) opaque;
1249    int i, j, src;
1250
1251    if (s->text_x[0] <= s->text_x[1]) {
1252        src = (s->y_base + s->text_y[0]) * s->width;
1253        chardata += s->text_y[0] * s->width;
1254        for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1255            for (j = 0; j < s->width; j++, src++) {
1256                console_write_ch(chardata ++,
1257                                 ATTR2CHTYPE(s->cells[src].ch,
1258                                             s->cells[src].t_attrib.fgcol,
1259                                             s->cells[src].t_attrib.bgcol,
1260                                             s->cells[src].t_attrib.bold));
1261            }
1262        dpy_text_update(s, s->text_x[0], s->text_y[0],
1263                        s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1264        s->text_x[0] = s->width;
1265        s->text_y[0] = s->height;
1266        s->text_x[1] = 0;
1267        s->text_y[1] = 0;
1268    }
1269    if (s->cursor_invalidate) {
1270        dpy_text_cursor(s, s->x, s->y);
1271        s->cursor_invalidate = 0;
1272    }
1273}
1274
1275static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
1276                                uint32_t head)
1277{
1278    Object *obj;
1279    QemuConsole *s;
1280    int i;
1281
1282    obj = object_new(TYPE_QEMU_CONSOLE);
1283    s = QEMU_CONSOLE(obj);
1284    s->head = head;
1285    object_property_add_link(obj, "device", TYPE_DEVICE,
1286                             (Object **)&s->device,
1287                             object_property_allow_set_link,
1288                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
1289                             &error_abort);
1290    object_property_add_uint32_ptr(obj, "head",
1291                                   &s->head, &error_abort);
1292
1293    if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1294        (console_type == GRAPHIC_CONSOLE))) {
1295        active_console = s;
1296    }
1297    s->ds = ds;
1298    s->console_type = console_type;
1299
1300    consoles = g_realloc(consoles, sizeof(*consoles) * (nb_consoles+1));
1301    if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) {
1302        s->index = nb_consoles;
1303        consoles[nb_consoles++] = s;
1304    } else {
1305        /*
1306         * HACK: Put graphical consoles before text consoles.
1307         *
1308         * Only do that for coldplugged devices.  After initial device
1309         * initialization we will not renumber the consoles any more.
1310         */
1311        for (i = nb_consoles; i > 0; i--) {
1312            if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1313                break;
1314            consoles[i] = consoles[i - 1];
1315            consoles[i]->index = i;
1316        }
1317        s->index = i;
1318        consoles[i] = s;
1319        nb_consoles++;
1320    }
1321    return s;
1322}
1323
1324static void qemu_alloc_display(DisplaySurface *surface, int width, int height)
1325{
1326    qemu_pixman_image_unref(surface->image);
1327    surface->image = NULL;
1328
1329    surface->format = PIXMAN_x8r8g8b8;
1330    surface->image = pixman_image_create_bits(surface->format,
1331                                              width, height,
1332                                              NULL, width * 4);
1333    assert(surface->image != NULL);
1334
1335    surface->flags = QEMU_ALLOCATED_FLAG;
1336}
1337
1338DisplaySurface *qemu_create_displaysurface(int width, int height)
1339{
1340    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1341
1342    trace_displaysurface_create(surface, width, height);
1343    qemu_alloc_display(surface, width, height);
1344    return surface;
1345}
1346
1347DisplaySurface *qemu_create_displaysurface_from(int width, int height,
1348                                                pixman_format_code_t format,
1349                                                int linesize, uint8_t *data)
1350{
1351    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1352
1353    trace_displaysurface_create_from(surface, width, height, format);
1354    surface->format = format;
1355    surface->image = pixman_image_create_bits(surface->format,
1356                                              width, height,
1357                                              (void *)data, linesize);
1358    assert(surface->image != NULL);
1359
1360    return surface;
1361}
1362
1363DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
1364{
1365    DisplaySurface *surface = g_new0(DisplaySurface, 1);
1366
1367    trace_displaysurface_create_pixman(surface);
1368    surface->format = pixman_image_get_format(image);
1369    surface->image = pixman_image_ref(image);
1370
1371    return surface;
1372}
1373
1374static void qemu_unmap_displaysurface_guestmem(pixman_image_t *image,
1375                                               void *unused)
1376{
1377    void *data = pixman_image_get_data(image);
1378    uint32_t size = pixman_image_get_stride(image) *
1379        pixman_image_get_height(image);
1380    cpu_physical_memory_unmap(data, size, 0, 0);
1381}
1382
1383DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height,
1384                                                    pixman_format_code_t format,
1385                                                    int linesize, uint64_t addr)
1386{
1387    DisplaySurface *surface;
1388    hwaddr size;
1389    void *data;
1390
1391    if (linesize == 0) {
1392        linesize = width * PIXMAN_FORMAT_BPP(format) / 8;
1393    }
1394
1395    size = (hwaddr)linesize * height;
1396    data = cpu_physical_memory_map(addr, &size, 0);
1397    if (size != (hwaddr)linesize * height) {
1398        cpu_physical_memory_unmap(data, size, 0, 0);
1399        return NULL;
1400    }
1401
1402    surface = qemu_create_displaysurface_from
1403        (width, height, format, linesize, data);
1404    pixman_image_set_destroy_function
1405        (surface->image, qemu_unmap_displaysurface_guestmem, NULL);
1406
1407    return surface;
1408}
1409
1410DisplaySurface *qemu_create_message_surface(int w, int h,
1411                                            const char *msg)
1412{
1413    DisplaySurface *surface = qemu_create_displaysurface(w, h);
1414    pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
1415    pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
1416    pixman_image_t *glyph;
1417    int len, x, y, i;
1418
1419    len = strlen(msg);
1420    x = (w / FONT_WIDTH  - len) / 2;
1421    y = (h / FONT_HEIGHT - 1)   / 2;
1422    for (i = 0; i < len; i++) {
1423        glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1424        qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1425                                 x+i, y, FONT_WIDTH, FONT_HEIGHT);
1426        qemu_pixman_image_unref(glyph);
1427    }
1428    return surface;
1429}
1430
1431void qemu_free_displaysurface(DisplaySurface *surface)
1432{
1433    if (surface == NULL) {
1434        return;
1435    }
1436    trace_displaysurface_free(surface);
1437    qemu_pixman_image_unref(surface->image);
1438    g_free(surface);
1439}
1440
1441bool console_has_gl(QemuConsole *con)
1442{
1443    return con->gl != NULL;
1444}
1445
1446bool console_has_gl_dmabuf(QemuConsole *con)
1447{
1448    return con->gl != NULL && con->gl->ops->dpy_gl_scanout_dmabuf != NULL;
1449}
1450
1451void register_displaychangelistener(DisplayChangeListener *dcl)
1452{
1453    static const char nodev[] =
1454        "This VM has no graphic display device.";
1455    static DisplaySurface *dummy;
1456    QemuConsole *con;
1457
1458    assert(!dcl->ds);
1459
1460    if (dcl->ops->dpy_gl_ctx_create) {
1461        /* display has opengl support */
1462        assert(dcl->con);
1463        if (dcl->con->gl) {
1464            fprintf(stderr, "can't register two opengl displays (%s, %s)\n",
1465                    dcl->ops->dpy_name, dcl->con->gl->ops->dpy_name);
1466            exit(1);
1467        }
1468        dcl->con->gl = dcl;
1469    }
1470
1471    trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
1472    dcl->ds = get_alloc_displaystate();
1473    QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1474    gui_setup_refresh(dcl->ds);
1475    if (dcl->con) {
1476        dcl->con->dcls++;
1477        con = dcl->con;
1478    } else {
1479        con = active_console;
1480    }
1481    if (dcl->ops->dpy_gfx_switch) {
1482        if (con) {
1483            dcl->ops->dpy_gfx_switch(dcl, con->surface);
1484        } else {
1485            if (!dummy) {
1486                dummy = qemu_create_message_surface(640, 480, nodev);
1487            }
1488            dcl->ops->dpy_gfx_switch(dcl, dummy);
1489        }
1490    }
1491    text_console_update_cursor(NULL);
1492}
1493
1494void update_displaychangelistener(DisplayChangeListener *dcl,
1495                                  uint64_t interval)
1496{
1497    DisplayState *ds = dcl->ds;
1498
1499    dcl->update_interval = interval;
1500    if (!ds->refreshing && ds->update_interval > interval) {
1501        timer_mod(ds->gui_timer, ds->last_update + interval);
1502    }
1503}
1504
1505void unregister_displaychangelistener(DisplayChangeListener *dcl)
1506{
1507    DisplayState *ds = dcl->ds;
1508    trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
1509    if (dcl->con) {
1510        dcl->con->dcls--;
1511    }
1512    QLIST_REMOVE(dcl, next);
1513    dcl->ds = NULL;
1514    gui_setup_refresh(ds);
1515}
1516
1517static void dpy_set_ui_info_timer(void *opaque)
1518{
1519    QemuConsole *con = opaque;
1520
1521    con->hw_ops->ui_info(con->hw, con->head, &con->ui_info);
1522}
1523
1524bool dpy_ui_info_supported(QemuConsole *con)
1525{
1526    return con->hw_ops->ui_info != NULL;
1527}
1528
1529int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info)
1530{
1531    assert(con != NULL);
1532
1533    if (!dpy_ui_info_supported(con)) {
1534        return -1;
1535    }
1536    if (memcmp(&con->ui_info, info, sizeof(con->ui_info)) == 0) {
1537        /* nothing changed -- ignore */
1538        return 0;
1539    }
1540
1541    /*
1542     * Typically we get a flood of these as the user resizes the window.
1543     * Wait until the dust has settled (one second without updates), then
1544     * go notify the guest.
1545     */
1546    con->ui_info = *info;
1547    timer_mod(con->ui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
1548    return 0;
1549}
1550
1551void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
1552{
1553    DisplayState *s = con->ds;
1554    DisplayChangeListener *dcl;
1555    int width = w;
1556    int height = h;
1557
1558    if (con->surface) {
1559        width = surface_width(con->surface);
1560        height = surface_height(con->surface);
1561    }
1562    x = MAX(x, 0);
1563    y = MAX(y, 0);
1564    x = MIN(x, width);
1565    y = MIN(y, height);
1566    w = MIN(w, width - x);
1567    h = MIN(h, height - y);
1568
1569    if (!qemu_console_is_visible(con)) {
1570        return;
1571    }
1572    QLIST_FOREACH(dcl, &s->listeners, next) {
1573        if (con != (dcl->con ? dcl->con : active_console)) {
1574            continue;
1575        }
1576        if (dcl->ops->dpy_gfx_update) {
1577            dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
1578        }
1579    }
1580}
1581
1582void dpy_gfx_replace_surface(QemuConsole *con,
1583                             DisplaySurface *surface)
1584{
1585    DisplayState *s = con->ds;
1586    DisplaySurface *old_surface = con->surface;
1587    DisplayChangeListener *dcl;
1588
1589    assert(old_surface != surface || surface == NULL);
1590
1591    con->surface = surface;
1592    QLIST_FOREACH(dcl, &s->listeners, next) {
1593        if (con != (dcl->con ? dcl->con : active_console)) {
1594            continue;
1595        }
1596        if (dcl->ops->dpy_gfx_switch) {
1597            dcl->ops->dpy_gfx_switch(dcl, surface);
1598        }
1599    }
1600    qemu_free_displaysurface(old_surface);
1601}
1602
1603bool dpy_gfx_check_format(QemuConsole *con,
1604                          pixman_format_code_t format)
1605{
1606    DisplayChangeListener *dcl;
1607    DisplayState *s = con->ds;
1608
1609    QLIST_FOREACH(dcl, &s->listeners, next) {
1610        if (dcl->con && dcl->con != con) {
1611            /* dcl bound to another console -> skip */
1612            continue;
1613        }
1614        if (dcl->ops->dpy_gfx_check_format) {
1615            if (!dcl->ops->dpy_gfx_check_format(dcl, format)) {
1616                return false;
1617            }
1618        } else {
1619            /* default is to whitelist native 32 bpp only */
1620            if (format != qemu_default_pixman_format(32, true)) {
1621                return false;
1622            }
1623        }
1624    }
1625    return true;
1626}
1627
1628static void dpy_refresh(DisplayState *s)
1629{
1630    DisplayChangeListener *dcl;
1631
1632    QLIST_FOREACH(dcl, &s->listeners, next) {
1633        if (dcl->ops->dpy_refresh) {
1634            dcl->ops->dpy_refresh(dcl);
1635        }
1636    }
1637}
1638
1639void dpy_text_cursor(QemuConsole *con, int x, int y)
1640{
1641    DisplayState *s = con->ds;
1642    DisplayChangeListener *dcl;
1643
1644    if (!qemu_console_is_visible(con)) {
1645        return;
1646    }
1647    QLIST_FOREACH(dcl, &s->listeners, next) {
1648        if (con != (dcl->con ? dcl->con : active_console)) {
1649            continue;
1650        }
1651        if (dcl->ops->dpy_text_cursor) {
1652            dcl->ops->dpy_text_cursor(dcl, x, y);
1653        }
1654    }
1655}
1656
1657void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
1658{
1659    DisplayState *s = con->ds;
1660    DisplayChangeListener *dcl;
1661
1662    if (!qemu_console_is_visible(con)) {
1663        return;
1664    }
1665    QLIST_FOREACH(dcl, &s->listeners, next) {
1666        if (con != (dcl->con ? dcl->con : active_console)) {
1667            continue;
1668        }
1669        if (dcl->ops->dpy_text_update) {
1670            dcl->ops->dpy_text_update(dcl, x, y, w, h);
1671        }
1672    }
1673}
1674
1675void dpy_text_resize(QemuConsole *con, int w, int h)
1676{
1677    DisplayState *s = con->ds;
1678    DisplayChangeListener *dcl;
1679
1680    if (!qemu_console_is_visible(con)) {
1681        return;
1682    }
1683    QLIST_FOREACH(dcl, &s->listeners, next) {
1684        if (con != (dcl->con ? dcl->con : active_console)) {
1685            continue;
1686        }
1687        if (dcl->ops->dpy_text_resize) {
1688            dcl->ops->dpy_text_resize(dcl, w, h);
1689        }
1690    }
1691}
1692
1693void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
1694{
1695    DisplayState *s = con->ds;
1696    DisplayChangeListener *dcl;
1697
1698    if (!qemu_console_is_visible(con)) {
1699        return;
1700    }
1701    QLIST_FOREACH(dcl, &s->listeners, next) {
1702        if (con != (dcl->con ? dcl->con : active_console)) {
1703            continue;
1704        }
1705        if (dcl->ops->dpy_mouse_set) {
1706            dcl->ops->dpy_mouse_set(dcl, x, y, on);
1707        }
1708    }
1709}
1710
1711void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
1712{
1713    DisplayState *s = con->ds;
1714    DisplayChangeListener *dcl;
1715
1716    if (!qemu_console_is_visible(con)) {
1717        return;
1718    }
1719    QLIST_FOREACH(dcl, &s->listeners, next) {
1720        if (con != (dcl->con ? dcl->con : active_console)) {
1721            continue;
1722        }
1723        if (dcl->ops->dpy_cursor_define) {
1724            dcl->ops->dpy_cursor_define(dcl, cursor);
1725        }
1726    }
1727}
1728
1729bool dpy_cursor_define_supported(QemuConsole *con)
1730{
1731    DisplayState *s = con->ds;
1732    DisplayChangeListener *dcl;
1733
1734    QLIST_FOREACH(dcl, &s->listeners, next) {
1735        if (dcl->ops->dpy_cursor_define) {
1736            return true;
1737        }
1738    }
1739    return false;
1740}
1741
1742QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
1743                                struct QEMUGLParams *qparams)
1744{
1745    assert(con->gl);
1746    return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
1747}
1748
1749void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
1750{
1751    assert(con->gl);
1752    con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
1753}
1754
1755int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
1756{
1757    assert(con->gl);
1758    return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
1759}
1760
1761QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con)
1762{
1763    assert(con->gl);
1764    return con->gl->ops->dpy_gl_ctx_get_current(con->gl);
1765}
1766
1767void dpy_gl_scanout_disable(QemuConsole *con)
1768{
1769    assert(con->gl);
1770    if (con->gl->ops->dpy_gl_scanout_disable) {
1771        con->gl->ops->dpy_gl_scanout_disable(con->gl);
1772    } else {
1773        con->gl->ops->dpy_gl_scanout_texture(con->gl, 0, false, 0, 0,
1774                                             0, 0, 0, 0);
1775    }
1776}
1777
1778void dpy_gl_scanout_texture(QemuConsole *con,
1779                            uint32_t backing_id,
1780                            bool backing_y_0_top,
1781                            uint32_t backing_width,
1782                            uint32_t backing_height,
1783                            uint32_t x, uint32_t y,
1784                            uint32_t width, uint32_t height)
1785{
1786    assert(con->gl);
1787    con->gl->ops->dpy_gl_scanout_texture(con->gl, backing_id,
1788                                         backing_y_0_top,
1789                                         backing_width, backing_height,
1790                                         x, y, width, height);
1791}
1792
1793void dpy_gl_scanout_dmabuf(QemuConsole *con,
1794                           QemuDmaBuf *dmabuf)
1795{
1796    assert(con->gl);
1797    con->gl->ops->dpy_gl_scanout_dmabuf(con->gl, dmabuf);
1798}
1799
1800void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
1801                          bool have_hot, uint32_t hot_x, uint32_t hot_y)
1802{
1803    assert(con->gl);
1804
1805    if (con->gl->ops->dpy_gl_cursor_dmabuf) {
1806        con->gl->ops->dpy_gl_cursor_dmabuf(con->gl, dmabuf,
1807                                           have_hot, hot_x, hot_y);
1808    }
1809}
1810
1811void dpy_gl_cursor_position(QemuConsole *con,
1812                            uint32_t pos_x, uint32_t pos_y)
1813{
1814    assert(con->gl);
1815
1816    if (con->gl->ops->dpy_gl_cursor_position) {
1817        con->gl->ops->dpy_gl_cursor_position(con->gl, pos_x, pos_y);
1818    }
1819}
1820
1821void dpy_gl_release_dmabuf(QemuConsole *con,
1822                          QemuDmaBuf *dmabuf)
1823{
1824    assert(con->gl);
1825
1826    if (con->gl->ops->dpy_gl_release_dmabuf) {
1827        con->gl->ops->dpy_gl_release_dmabuf(con->gl, dmabuf);
1828    }
1829}
1830
1831void dpy_gl_update(QemuConsole *con,
1832                   uint32_t x, uint32_t y, uint32_t w, uint32_t h)
1833{
1834    assert(con->gl);
1835    con->gl->ops->dpy_gl_update(con->gl, x, y, w, h);
1836}
1837
1838/***********************************************************/
1839/* register display */
1840
1841/* console.c internal use only */
1842static DisplayState *get_alloc_displaystate(void)
1843{
1844    if (!display_state) {
1845        display_state = g_new0(DisplayState, 1);
1846        cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1847                                    text_console_update_cursor, NULL);
1848    }
1849    return display_state;
1850}
1851
1852/*
1853 * Called by main(), after creating QemuConsoles
1854 * and before initializing ui (sdl/vnc/...).
1855 */
1856DisplayState *init_displaystate(void)
1857{
1858    gchar *name;
1859    int i;
1860
1861    get_alloc_displaystate();
1862    for (i = 0; i < nb_consoles; i++) {
1863        if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
1864            consoles[i]->ds == NULL) {
1865            text_console_do_init(consoles[i]->chr, display_state);
1866        }
1867
1868        /* Hook up into the qom tree here (not in new_console()), once
1869         * all QemuConsoles are created and the order / numbering
1870         * doesn't change any more */
1871        name = g_strdup_printf("console[%d]", i);
1872        object_property_add_child(container_get(object_get_root(), "/backend"),
1873                                  name, OBJECT(consoles[i]), &error_abort);
1874        g_free(name);
1875    }
1876
1877    return display_state;
1878}
1879
1880void graphic_console_set_hwops(QemuConsole *con,
1881                               const GraphicHwOps *hw_ops,
1882                               void *opaque)
1883{
1884    con->hw_ops = hw_ops;
1885    con->hw = opaque;
1886}
1887
1888QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
1889                                  const GraphicHwOps *hw_ops,
1890                                  void *opaque)
1891{
1892    static const char noinit[] =
1893        "Guest has not initialized the display (yet).";
1894    int width = 640;
1895    int height = 480;
1896    QemuConsole *s;
1897    DisplayState *ds;
1898    DisplaySurface *surface;
1899
1900    ds = get_alloc_displaystate();
1901    s = qemu_console_lookup_unused();
1902    if (s) {
1903        trace_console_gfx_reuse(s->index);
1904        if (s->surface) {
1905            width = surface_width(s->surface);
1906            height = surface_height(s->surface);
1907        }
1908    } else {
1909        trace_console_gfx_new();
1910        s = new_console(ds, GRAPHIC_CONSOLE, head);
1911        s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1912                                   dpy_set_ui_info_timer, s);
1913    }
1914    graphic_console_set_hwops(s, hw_ops, opaque);
1915    if (dev) {
1916        object_property_set_link(OBJECT(s), OBJECT(dev), "device",
1917                                 &error_abort);
1918    }
1919
1920    surface = qemu_create_message_surface(width, height, noinit);
1921    dpy_gfx_replace_surface(s, surface);
1922    return s;
1923}
1924
1925static const GraphicHwOps unused_ops = {
1926    /* no callbacks */
1927};
1928
1929void graphic_console_close(QemuConsole *con)
1930{
1931    static const char unplugged[] =
1932        "Guest display has been unplugged";
1933    DisplaySurface *surface;
1934    int width = 640;
1935    int height = 480;
1936
1937    if (con->surface) {
1938        width = surface_width(con->surface);
1939        height = surface_height(con->surface);
1940    }
1941
1942    trace_console_gfx_close(con->index);
1943    object_property_set_link(OBJECT(con), NULL, "device", &error_abort);
1944    graphic_console_set_hwops(con, &unused_ops, NULL);
1945
1946    if (con->gl) {
1947        dpy_gl_scanout_disable(con);
1948    }
1949    surface = qemu_create_message_surface(width, height, unplugged);
1950    dpy_gfx_replace_surface(con, surface);
1951}
1952
1953QemuConsole *qemu_console_lookup_by_index(unsigned int index)
1954{
1955    if (index >= nb_consoles) {
1956        return NULL;
1957    }
1958    return consoles[index];
1959}
1960
1961QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
1962{
1963    Object *obj;
1964    uint32_t h;
1965    int i;
1966
1967    for (i = 0; i < nb_consoles; i++) {
1968        if (!consoles[i]) {
1969            continue;
1970        }
1971        obj = object_property_get_link(OBJECT(consoles[i]),
1972                                       "device", &error_abort);
1973        if (DEVICE(obj) != dev) {
1974            continue;
1975        }
1976        h = object_property_get_uint(OBJECT(consoles[i]),
1977                                     "head", &error_abort);
1978        if (h != head) {
1979            continue;
1980        }
1981        return consoles[i];
1982    }
1983    return NULL;
1984}
1985
1986QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
1987                                                uint32_t head, Error **errp)
1988{
1989    DeviceState *dev;
1990    QemuConsole *con;
1991
1992    dev = qdev_find_recursive(sysbus_get_default(), device_id);
1993    if (dev == NULL) {
1994        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
1995                  "Device '%s' not found", device_id);
1996        return NULL;
1997    }
1998
1999    con = qemu_console_lookup_by_device(dev, head);
2000    if (con == NULL) {
2001        error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
2002                   device_id, head);
2003        return NULL;
2004    }
2005
2006    return con;
2007}
2008
2009QemuConsole *qemu_console_lookup_unused(void)
2010{
2011    Object *obj;
2012    int i;
2013
2014    for (i = 0; i < nb_consoles; i++) {
2015        if (!consoles[i]) {
2016            continue;
2017        }
2018        if (consoles[i]->hw_ops != &unused_ops) {
2019            continue;
2020        }
2021        obj = object_property_get_link(OBJECT(consoles[i]),
2022                                       "device", &error_abort);
2023        if (obj != NULL) {
2024            continue;
2025        }
2026        return consoles[i];
2027    }
2028    return NULL;
2029}
2030
2031bool qemu_console_is_visible(QemuConsole *con)
2032{
2033    return (con == active_console) || (con->dcls > 0);
2034}
2035
2036bool qemu_console_is_graphic(QemuConsole *con)
2037{
2038    if (con == NULL) {
2039        con = active_console;
2040    }
2041    return con && (con->console_type == GRAPHIC_CONSOLE);
2042}
2043
2044bool qemu_console_is_fixedsize(QemuConsole *con)
2045{
2046    if (con == NULL) {
2047        con = active_console;
2048    }
2049    return con && (con->console_type != TEXT_CONSOLE);
2050}
2051
2052bool qemu_console_is_gl_blocked(QemuConsole *con)
2053{
2054    assert(con != NULL);
2055    return con->gl_block;
2056}
2057
2058char *qemu_console_get_label(QemuConsole *con)
2059{
2060    if (con->console_type == GRAPHIC_CONSOLE) {
2061        if (con->device) {
2062            return g_strdup(object_get_typename(con->device));
2063        }
2064        return g_strdup("VGA");
2065    } else {
2066        if (con->chr && con->chr->label) {
2067            return g_strdup(con->chr->label);
2068        }
2069        return g_strdup_printf("vc%d", con->index);
2070    }
2071}
2072
2073int qemu_console_get_index(QemuConsole *con)
2074{
2075    if (con == NULL) {
2076        con = active_console;
2077    }
2078    return con ? con->index : -1;
2079}
2080
2081uint32_t qemu_console_get_head(QemuConsole *con)
2082{
2083    if (con == NULL) {
2084        con = active_console;
2085    }
2086    return con ? con->head : -1;
2087}
2088
2089QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con)
2090{
2091    assert(con != NULL);
2092    return &con->ui_info;
2093}
2094
2095int qemu_console_get_width(QemuConsole *con, int fallback)
2096{
2097    if (con == NULL) {
2098        con = active_console;
2099    }
2100    return con ? surface_width(con->surface) : fallback;
2101}
2102
2103int qemu_console_get_height(QemuConsole *con, int fallback)
2104{
2105    if (con == NULL) {
2106        con = active_console;
2107    }
2108    return con ? surface_height(con->surface) : fallback;
2109}
2110
2111static void vc_chr_set_echo(Chardev *chr, bool echo)
2112{
2113    VCChardev *drv = VC_CHARDEV(chr);
2114    QemuConsole *s = drv->console;
2115
2116    s->echo = echo;
2117}
2118
2119static void text_console_update_cursor_timer(void)
2120{
2121    timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
2122              + CONSOLE_CURSOR_PERIOD / 2);
2123}
2124
2125static void text_console_update_cursor(void *opaque)
2126{
2127    QemuConsole *s;
2128    int i, count = 0;
2129
2130    cursor_visible_phase = !cursor_visible_phase;
2131
2132    for (i = 0; i < nb_consoles; i++) {
2133        s = consoles[i];
2134        if (qemu_console_is_graphic(s) ||
2135            !qemu_console_is_visible(s)) {
2136            continue;
2137        }
2138        count++;
2139        graphic_hw_invalidate(s);
2140    }
2141
2142    if (count) {
2143        text_console_update_cursor_timer();
2144    }
2145}
2146
2147static const GraphicHwOps text_console_ops = {
2148    .invalidate  = text_console_invalidate,
2149    .text_update = text_console_update,
2150};
2151
2152static void text_console_do_init(Chardev *chr, DisplayState *ds)
2153{
2154    VCChardev *drv = VC_CHARDEV(chr);
2155    QemuConsole *s = drv->console;
2156    int g_width = 80 * FONT_WIDTH;
2157    int g_height = 24 * FONT_HEIGHT;
2158
2159    s->out_fifo.buf = s->out_fifo_buf;
2160    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
2161    s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
2162    s->ds = ds;
2163
2164    s->y_displayed = 0;
2165    s->y_base = 0;
2166    s->total_height = DEFAULT_BACKSCROLL;
2167    s->x = 0;
2168    s->y = 0;
2169    if (!s->surface) {
2170        if (active_console && active_console->surface) {
2171            g_width = surface_width(active_console->surface);
2172            g_height = surface_height(active_console->surface);
2173        }
2174        s->surface = qemu_create_displaysurface(g_width, g_height);
2175    }
2176
2177    s->hw_ops = &text_console_ops;
2178    s->hw = s;
2179
2180    /* Set text attribute defaults */
2181    s->t_attrib_default.bold = 0;
2182    s->t_attrib_default.uline = 0;
2183    s->t_attrib_default.blink = 0;
2184    s->t_attrib_default.invers = 0;
2185    s->t_attrib_default.unvisible = 0;
2186    s->t_attrib_default.fgcol = QEMU_COLOR_WHITE;
2187    s->t_attrib_default.bgcol = QEMU_COLOR_BLACK;
2188    /* set current text attributes to default */
2189    s->t_attrib = s->t_attrib_default;
2190    text_console_resize(s);
2191
2192    if (chr->label) {
2193        char msg[128];
2194        int len;
2195
2196        s->t_attrib.bgcol = QEMU_COLOR_BLUE;
2197        len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
2198        vc_chr_write(chr, (uint8_t *)msg, len);
2199        s->t_attrib = s->t_attrib_default;
2200    }
2201
2202    qemu_chr_be_event(chr, CHR_EVENT_OPENED);
2203}
2204
2205static void vc_chr_open(Chardev *chr,
2206                        ChardevBackend *backend,
2207                        bool *be_opened,
2208                        Error **errp)
2209{
2210    ChardevVC *vc = backend->u.vc.data;
2211    VCChardev *drv = VC_CHARDEV(chr);
2212    QemuConsole *s;
2213    unsigned width = 0;
2214    unsigned height = 0;
2215
2216    if (vc->has_width) {
2217        width = vc->width;
2218    } else if (vc->has_cols) {
2219        width = vc->cols * FONT_WIDTH;
2220    }
2221
2222    if (vc->has_height) {
2223        height = vc->height;
2224    } else if (vc->has_rows) {
2225        height = vc->rows * FONT_HEIGHT;
2226    }
2227
2228    trace_console_txt_new(width, height);
2229    if (width == 0 || height == 0) {
2230        s = new_console(NULL, TEXT_CONSOLE, 0);
2231    } else {
2232        s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0);
2233        s->surface = qemu_create_displaysurface(width, height);
2234    }
2235
2236    if (!s) {
2237        error_setg(errp, "cannot create text console");
2238        return;
2239    }
2240
2241    s->chr = chr;
2242    drv->console = s;
2243
2244    if (display_state) {
2245        text_console_do_init(chr, display_state);
2246    }
2247
2248    /* console/chardev init sometimes completes elsewhere in a 2nd
2249     * stage, so defer OPENED events until they are fully initialized
2250     */
2251    *be_opened = false;
2252}
2253
2254void qemu_console_resize(QemuConsole *s, int width, int height)
2255{
2256    DisplaySurface *surface;
2257
2258    assert(s->console_type == GRAPHIC_CONSOLE);
2259
2260    if (s->surface && (s->surface->flags & QEMU_ALLOCATED_FLAG) &&
2261        pixman_image_get_width(s->surface->image) == width &&
2262        pixman_image_get_height(s->surface->image) == height) {
2263        return;
2264    }
2265
2266    surface = qemu_create_displaysurface(width, height);
2267    dpy_gfx_replace_surface(s, surface);
2268}
2269
2270DisplaySurface *qemu_console_surface(QemuConsole *console)
2271{
2272    return console->surface;
2273}
2274
2275PixelFormat qemu_default_pixelformat(int bpp)
2276{
2277    pixman_format_code_t fmt = qemu_default_pixman_format(bpp, true);
2278    PixelFormat pf = qemu_pixelformat_from_pixman(fmt);
2279    return pf;
2280}
2281
2282static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
2283
2284void qemu_display_register(QemuDisplay *ui)
2285{
2286    assert(ui->type < DISPLAY_TYPE__MAX);
2287    dpys[ui->type] = ui;
2288}
2289
2290bool qemu_display_find_default(DisplayOptions *opts)
2291{
2292    static DisplayType prio[] = {
2293        DISPLAY_TYPE_GTK,
2294        DISPLAY_TYPE_SDL,
2295        DISPLAY_TYPE_COCOA
2296    };
2297    int i;
2298
2299    for (i = 0; i < ARRAY_SIZE(prio); i++) {
2300        if (dpys[prio[i]] == NULL) {
2301            ui_module_load_one(DisplayType_lookup.array[prio[i]]);
2302        }
2303        if (dpys[prio[i]] == NULL) {
2304            continue;
2305        }
2306        opts->type = prio[i];
2307        return true;
2308    }
2309    return false;
2310}
2311
2312void qemu_display_early_init(DisplayOptions *opts)
2313{
2314    assert(opts->type < DISPLAY_TYPE__MAX);
2315    if (opts->type == DISPLAY_TYPE_NONE) {
2316        return;
2317    }
2318    if (dpys[opts->type] == NULL) {
2319        ui_module_load_one(DisplayType_lookup.array[opts->type]);
2320    }
2321    if (dpys[opts->type] == NULL) {
2322        error_report("Display '%s' is not available.",
2323                     DisplayType_lookup.array[opts->type]);
2324        exit(1);
2325    }
2326    if (dpys[opts->type]->early_init) {
2327        dpys[opts->type]->early_init(opts);
2328    }
2329}
2330
2331void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
2332{
2333    assert(opts->type < DISPLAY_TYPE__MAX);
2334    if (opts->type == DISPLAY_TYPE_NONE) {
2335        return;
2336    }
2337    assert(dpys[opts->type] != NULL);
2338    dpys[opts->type]->init(ds, opts);
2339}
2340
2341void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
2342{
2343    int val;
2344    ChardevVC *vc;
2345
2346    backend->type = CHARDEV_BACKEND_KIND_VC;
2347    vc = backend->u.vc.data = g_new0(ChardevVC, 1);
2348    qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
2349
2350    val = qemu_opt_get_number(opts, "width", 0);
2351    if (val != 0) {
2352        vc->has_width = true;
2353        vc->width = val;
2354    }
2355
2356    val = qemu_opt_get_number(opts, "height", 0);
2357    if (val != 0) {
2358        vc->has_height = true;
2359        vc->height = val;
2360    }
2361
2362    val = qemu_opt_get_number(opts, "cols", 0);
2363    if (val != 0) {
2364        vc->has_cols = true;
2365        vc->cols = val;
2366    }
2367
2368    val = qemu_opt_get_number(opts, "rows", 0);
2369    if (val != 0) {
2370        vc->has_rows = true;
2371        vc->rows = val;
2372    }
2373}
2374
2375static const TypeInfo qemu_console_info = {
2376    .name = TYPE_QEMU_CONSOLE,
2377    .parent = TYPE_OBJECT,
2378    .instance_size = sizeof(QemuConsole),
2379    .class_size = sizeof(QemuConsoleClass),
2380};
2381
2382static void char_vc_class_init(ObjectClass *oc, void *data)
2383{
2384    ChardevClass *cc = CHARDEV_CLASS(oc);
2385
2386    cc->parse = qemu_chr_parse_vc;
2387    cc->open = vc_chr_open;
2388    cc->chr_write = vc_chr_write;
2389    cc->chr_set_echo = vc_chr_set_echo;
2390}
2391
2392static const TypeInfo char_vc_type_info = {
2393    .name = TYPE_CHARDEV_VC,
2394    .parent = TYPE_CHARDEV,
2395    .instance_size = sizeof(VCChardev),
2396    .class_init = char_vc_class_init,
2397};
2398
2399void qemu_console_early_init(void)
2400{
2401    /* set the default vc driver */
2402    if (!object_class_by_name(TYPE_CHARDEV_VC)) {
2403        type_register(&char_vc_type_info);
2404    }
2405}
2406
2407static void register_types(void)
2408{
2409    type_register_static(&qemu_console_info);
2410}
2411
2412type_init(register_types);
2413