qemu/hw/display/qxl-render.c
<<
>>
Prefs
   1/*
   2 * qxl local rendering (aka display on sdl/vnc)
   3 *
   4 * Copyright (C) 2010 Red Hat, Inc.
   5 *
   6 * maintained by Gerd Hoffmann <kraxel@redhat.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 or
  11 * (at your option) version 3 of the License.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  20 */
  21
  22#include "qxl.h"
  23
  24static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
  25{
  26    DisplaySurface *surface = qemu_console_surface(qxl->vga.con);
  27    uint8_t *dst = surface_data(surface);
  28    uint8_t *src;
  29    int len, i;
  30
  31    if (is_buffer_shared(surface)) {
  32        return;
  33    }
  34    trace_qxl_render_blit(qxl->guest_primary.qxl_stride,
  35            rect->left, rect->right, rect->top, rect->bottom);
  36    src = qxl->guest_primary.data;
  37    if (qxl->guest_primary.qxl_stride < 0) {
  38        /* qxl surface is upside down, walk src scanlines
  39         * in reverse order to flip it */
  40        src += (qxl->guest_primary.surface.height - rect->top - 1) *
  41            qxl->guest_primary.abs_stride;
  42    } else {
  43        src += rect->top * qxl->guest_primary.abs_stride;
  44    }
  45    dst += rect->top  * qxl->guest_primary.abs_stride;
  46    src += rect->left * qxl->guest_primary.bytes_pp;
  47    dst += rect->left * qxl->guest_primary.bytes_pp;
  48    len  = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
  49
  50    for (i = rect->top; i < rect->bottom; i++) {
  51        memcpy(dst, src, len);
  52        dst += qxl->guest_primary.abs_stride;
  53        src += qxl->guest_primary.qxl_stride;
  54    }
  55}
  56
  57void qxl_render_resize(PCIQXLDevice *qxl)
  58{
  59    QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
  60
  61    qxl->guest_primary.qxl_stride = sc->stride;
  62    qxl->guest_primary.abs_stride = abs(sc->stride);
  63    qxl->guest_primary.resized++;
  64    switch (sc->format) {
  65    case SPICE_SURFACE_FMT_16_555:
  66        qxl->guest_primary.bytes_pp = 2;
  67        qxl->guest_primary.bits_pp = 15;
  68        break;
  69    case SPICE_SURFACE_FMT_16_565:
  70        qxl->guest_primary.bytes_pp = 2;
  71        qxl->guest_primary.bits_pp = 16;
  72        break;
  73    case SPICE_SURFACE_FMT_32_xRGB:
  74    case SPICE_SURFACE_FMT_32_ARGB:
  75        qxl->guest_primary.bytes_pp = 4;
  76        qxl->guest_primary.bits_pp = 32;
  77        break;
  78    default:
  79        fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__,
  80                qxl->guest_primary.surface.format);
  81        qxl->guest_primary.bytes_pp = 4;
  82        qxl->guest_primary.bits_pp = 32;
  83        break;
  84    }
  85}
  86
  87static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
  88{
  89    area->left   = 0;
  90    area->right  = qxl->guest_primary.surface.width;
  91    area->top    = 0;
  92    area->bottom = qxl->guest_primary.surface.height;
  93}
  94
  95static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
  96{
  97    VGACommonState *vga = &qxl->vga;
  98    DisplaySurface *surface;
  99    int i;
 100
 101    if (qxl->guest_primary.resized) {
 102        qxl->guest_primary.resized = 0;
 103        qxl->guest_primary.data = qxl_phys2virt(qxl,
 104                                                qxl->guest_primary.surface.mem,
 105                                                MEMSLOT_GROUP_GUEST);
 106        if (!qxl->guest_primary.data) {
 107            return;
 108        }
 109        qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
 110        qxl->num_dirty_rects = 1;
 111        trace_qxl_render_guest_primary_resized(
 112               qxl->guest_primary.surface.width,
 113               qxl->guest_primary.surface.height,
 114               qxl->guest_primary.qxl_stride,
 115               qxl->guest_primary.bytes_pp,
 116               qxl->guest_primary.bits_pp);
 117        if (qxl->guest_primary.qxl_stride > 0) {
 118            surface = qemu_create_displaysurface_from
 119                (qxl->guest_primary.surface.width,
 120                 qxl->guest_primary.surface.height,
 121                 qxl->guest_primary.bits_pp,
 122                 qxl->guest_primary.abs_stride,
 123                 qxl->guest_primary.data,
 124                 false);
 125        } else {
 126            surface = qemu_create_displaysurface
 127                (qxl->guest_primary.surface.width,
 128                 qxl->guest_primary.surface.height);
 129        }
 130        dpy_gfx_replace_surface(vga->con, surface);
 131    }
 132
 133    if (!qxl->guest_primary.data) {
 134        return;
 135    }
 136    for (i = 0; i < qxl->num_dirty_rects; i++) {
 137        if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
 138            break;
 139        }
 140        qxl_blit(qxl, qxl->dirty+i);
 141        dpy_gfx_update(vga->con,
 142                       qxl->dirty[i].left, qxl->dirty[i].top,
 143                       qxl->dirty[i].right - qxl->dirty[i].left,
 144                       qxl->dirty[i].bottom - qxl->dirty[i].top);
 145    }
 146    qxl->num_dirty_rects = 0;
 147}
 148
 149/*
 150 * use ssd.lock to protect render_update_cookie_num.
 151 * qxl_render_update is called by io thread or vcpu thread, and the completion
 152 * callbacks are called by spice_server thread, defering to bh called from the
 153 * io thread.
 154 */
 155void qxl_render_update(PCIQXLDevice *qxl)
 156{
 157    QXLCookie *cookie;
 158
 159    qemu_mutex_lock(&qxl->ssd.lock);
 160
 161    if (!runstate_is_running() || !qxl->guest_primary.commands) {
 162        qxl_render_update_area_unlocked(qxl);
 163        qemu_mutex_unlock(&qxl->ssd.lock);
 164        return;
 165    }
 166
 167    qxl->guest_primary.commands = 0;
 168    qxl->render_update_cookie_num++;
 169    qemu_mutex_unlock(&qxl->ssd.lock);
 170    cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
 171                            0);
 172    qxl_set_rect_to_surface(qxl, &cookie->u.render.area);
 173    qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL,
 174                          0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie);
 175}
 176
 177void qxl_render_update_area_bh(void *opaque)
 178{
 179    PCIQXLDevice *qxl = opaque;
 180
 181    qemu_mutex_lock(&qxl->ssd.lock);
 182    qxl_render_update_area_unlocked(qxl);
 183    qemu_mutex_unlock(&qxl->ssd.lock);
 184}
 185
 186void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
 187{
 188    qemu_mutex_lock(&qxl->ssd.lock);
 189    trace_qxl_render_update_area_done(cookie);
 190    qemu_bh_schedule(qxl->update_area_bh);
 191    qxl->render_update_cookie_num--;
 192    qemu_mutex_unlock(&qxl->ssd.lock);
 193    g_free(cookie);
 194}
 195
 196static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
 197{
 198    QEMUCursor *c;
 199    uint8_t *image, *mask;
 200    size_t size;
 201
 202    c = cursor_alloc(cursor->header.width, cursor->header.height);
 203    c->hot_x = cursor->header.hot_spot_x;
 204    c->hot_y = cursor->header.hot_spot_y;
 205    switch (cursor->header.type) {
 206    case SPICE_CURSOR_TYPE_ALPHA:
 207        size = sizeof(uint32_t) * cursor->header.width * cursor->header.height;
 208        memcpy(c->data, cursor->chunk.data, size);
 209        if (qxl->debug > 2) {
 210            cursor_print_ascii_art(c, "qxl/alpha");
 211        }
 212        break;
 213    case SPICE_CURSOR_TYPE_MONO:
 214        mask  = cursor->chunk.data;
 215        image = mask + cursor_get_mono_bpl(c) * c->width;
 216        cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask);
 217        if (qxl->debug > 2) {
 218            cursor_print_ascii_art(c, "qxl/mono");
 219        }
 220        break;
 221    default:
 222        fprintf(stderr, "%s: not implemented: type %d\n",
 223                __FUNCTION__, cursor->header.type);
 224        goto fail;
 225    }
 226    return c;
 227
 228fail:
 229    cursor_put(c);
 230    return NULL;
 231}
 232
 233
 234/* called from spice server thread context only */
 235int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
 236{
 237    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
 238    QXLCursor *cursor;
 239    QEMUCursor *c;
 240
 241    if (!cmd) {
 242        return 1;
 243    }
 244
 245    if (!dpy_cursor_define_supported(qxl->vga.con)) {
 246        return 0;
 247    }
 248
 249    if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) {
 250        fprintf(stderr, "%s", __FUNCTION__);
 251        qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
 252        fprintf(stderr, "\n");
 253    }
 254    switch (cmd->type) {
 255    case QXL_CURSOR_SET:
 256        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
 257        if (!cursor) {
 258            return 1;
 259        }
 260        if (cursor->chunk.data_size != cursor->data_size) {
 261            fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__);
 262            return 1;
 263        }
 264        c = qxl_cursor(qxl, cursor);
 265        if (c == NULL) {
 266            c = cursor_builtin_left_ptr();
 267        }
 268        qemu_mutex_lock(&qxl->ssd.lock);
 269        if (qxl->ssd.cursor) {
 270            cursor_put(qxl->ssd.cursor);
 271        }
 272        qxl->ssd.cursor = c;
 273        qxl->ssd.mouse_x = cmd->u.set.position.x;
 274        qxl->ssd.mouse_y = cmd->u.set.position.y;
 275        qemu_mutex_unlock(&qxl->ssd.lock);
 276        break;
 277    case QXL_CURSOR_MOVE:
 278        qemu_mutex_lock(&qxl->ssd.lock);
 279        qxl->ssd.mouse_x = cmd->u.position.x;
 280        qxl->ssd.mouse_y = cmd->u.position.y;
 281        qemu_mutex_unlock(&qxl->ssd.lock);
 282        break;
 283    }
 284    return 0;
 285}
 286