qemu/hw/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_flip(PCIQXLDevice *qxl, QXLRect *rect)
  25{
  26    uint8_t *src = qxl->guest_primary.data;
  27    uint8_t *dst = qxl->guest_primary.flipped;
  28    int len, i;
  29
  30    src += (qxl->guest_primary.surface.height - rect->top - 1) *
  31        qxl->guest_primary.abs_stride;
  32    dst += rect->top  * qxl->guest_primary.abs_stride;
  33    src += rect->left * qxl->guest_primary.bytes_pp;
  34    dst += rect->left * qxl->guest_primary.bytes_pp;
  35    len  = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
  36
  37    for (i = rect->top; i < rect->bottom; i++) {
  38        memcpy(dst, src, len);
  39        dst += qxl->guest_primary.abs_stride;
  40        src -= qxl->guest_primary.abs_stride;
  41    }
  42}
  43
  44void qxl_render_resize(PCIQXLDevice *qxl)
  45{
  46    QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
  47
  48    qxl->guest_primary.qxl_stride = sc->stride;
  49    qxl->guest_primary.abs_stride = abs(sc->stride);
  50    qxl->guest_primary.resized++;
  51    switch (sc->format) {
  52    case SPICE_SURFACE_FMT_16_555:
  53        qxl->guest_primary.bytes_pp = 2;
  54        qxl->guest_primary.bits_pp = 15;
  55        break;
  56    case SPICE_SURFACE_FMT_16_565:
  57        qxl->guest_primary.bytes_pp = 2;
  58        qxl->guest_primary.bits_pp = 16;
  59        break;
  60    case SPICE_SURFACE_FMT_32_xRGB:
  61    case SPICE_SURFACE_FMT_32_ARGB:
  62        qxl->guest_primary.bytes_pp = 4;
  63        qxl->guest_primary.bits_pp = 32;
  64        break;
  65    default:
  66        fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__,
  67                qxl->guest_primary.surface.format);
  68        qxl->guest_primary.bytes_pp = 4;
  69        qxl->guest_primary.bits_pp = 32;
  70        break;
  71    }
  72}
  73
  74void qxl_render_update(PCIQXLDevice *qxl)
  75{
  76    VGACommonState *vga = &qxl->vga;
  77    QXLRect dirty[32], update;
  78    void *ptr;
  79    int i, redraw = 0;
  80
  81    if (!is_buffer_shared(vga->ds->surface)) {
  82        dprint(qxl, 1, "%s: restoring shared displaysurface\n", __func__);
  83        qxl->guest_primary.resized++;
  84        qxl->guest_primary.commands++;
  85        redraw = 1;
  86    }
  87
  88    if (qxl->guest_primary.resized) {
  89        qxl->guest_primary.resized = 0;
  90
  91        if (qxl->guest_primary.flipped) {
  92            g_free(qxl->guest_primary.flipped);
  93            qxl->guest_primary.flipped = NULL;
  94        }
  95        qemu_free_displaysurface(vga->ds);
  96
  97        qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
  98        if (qxl->guest_primary.qxl_stride < 0) {
  99            /* spice surface is upside down -> need extra buffer to flip */
 100            qxl->guest_primary.flipped =
 101                g_malloc(qxl->guest_primary.surface.width *
 102                         qxl->guest_primary.abs_stride);
 103            ptr = qxl->guest_primary.flipped;
 104        } else {
 105            ptr = qxl->guest_primary.data;
 106        }
 107        dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n",
 108               __FUNCTION__,
 109               qxl->guest_primary.surface.width,
 110               qxl->guest_primary.surface.height,
 111               qxl->guest_primary.qxl_stride,
 112               qxl->guest_primary.bytes_pp,
 113               qxl->guest_primary.bits_pp,
 114               qxl->guest_primary.flipped ? "yes" : "no");
 115        vga->ds->surface =
 116            qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
 117                                            qxl->guest_primary.surface.height,
 118                                            qxl->guest_primary.bits_pp,
 119                                            qxl->guest_primary.abs_stride,
 120                                            ptr);
 121        dpy_resize(vga->ds);
 122    }
 123
 124    if (!qxl->guest_primary.commands) {
 125        return;
 126    }
 127    qxl->guest_primary.commands = 0;
 128
 129    update.left   = 0;
 130    update.right  = qxl->guest_primary.surface.width;
 131    update.top    = 0;
 132    update.bottom = qxl->guest_primary.surface.height;
 133
 134    memset(dirty, 0, sizeof(dirty));
 135    qxl_spice_update_area(qxl, 0, &update,
 136                          dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC);
 137    if (redraw) {
 138        memset(dirty, 0, sizeof(dirty));
 139        dirty[0] = update;
 140    }
 141
 142    for (i = 0; i < ARRAY_SIZE(dirty); i++) {
 143        if (qemu_spice_rect_is_empty(dirty+i)) {
 144            break;
 145        }
 146        if (qxl->guest_primary.flipped) {
 147            qxl_flip(qxl, dirty+i);
 148        }
 149        dpy_update(vga->ds,
 150                   dirty[i].left, dirty[i].top,
 151                   dirty[i].right - dirty[i].left,
 152                   dirty[i].bottom - dirty[i].top);
 153    }
 154}
 155
 156static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
 157{
 158    QEMUCursor *c;
 159    uint8_t *image, *mask;
 160    int size;
 161
 162    c = cursor_alloc(cursor->header.width, cursor->header.height);
 163    c->hot_x = cursor->header.hot_spot_x;
 164    c->hot_y = cursor->header.hot_spot_y;
 165    switch (cursor->header.type) {
 166    case SPICE_CURSOR_TYPE_ALPHA:
 167        size = cursor->header.width * cursor->header.height * sizeof(uint32_t);
 168        memcpy(c->data, cursor->chunk.data, size);
 169        if (qxl->debug > 2) {
 170            cursor_print_ascii_art(c, "qxl/alpha");
 171        }
 172        break;
 173    case SPICE_CURSOR_TYPE_MONO:
 174        mask  = cursor->chunk.data;
 175        image = mask + cursor_get_mono_bpl(c) * c->width;
 176        cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask);
 177        if (qxl->debug > 2) {
 178            cursor_print_ascii_art(c, "qxl/mono");
 179        }
 180        break;
 181    default:
 182        fprintf(stderr, "%s: not implemented: type %d\n",
 183                __FUNCTION__, cursor->header.type);
 184        goto fail;
 185    }
 186    return c;
 187
 188fail:
 189    cursor_put(c);
 190    return NULL;
 191}
 192
 193
 194/* called from spice server thread context only */
 195void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
 196{
 197    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
 198    QXLCursor *cursor;
 199    QEMUCursor *c;
 200
 201    if (!qxl->ssd.ds->mouse_set || !qxl->ssd.ds->cursor_define) {
 202        return;
 203    }
 204
 205    if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) {
 206        fprintf(stderr, "%s", __FUNCTION__);
 207        qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
 208        fprintf(stderr, "\n");
 209    }
 210    switch (cmd->type) {
 211    case QXL_CURSOR_SET:
 212        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
 213        if (cursor->chunk.data_size != cursor->data_size) {
 214            fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__);
 215            return;
 216        }
 217        c = qxl_cursor(qxl, cursor);
 218        if (c == NULL) {
 219            c = cursor_builtin_left_ptr();
 220        }
 221        qemu_mutex_lock(&qxl->ssd.lock);
 222        if (qxl->ssd.cursor) {
 223            cursor_put(qxl->ssd.cursor);
 224        }
 225        qxl->ssd.cursor = c;
 226        qxl->ssd.mouse_x = cmd->u.set.position.x;
 227        qxl->ssd.mouse_y = cmd->u.set.position.y;
 228        qemu_mutex_unlock(&qxl->ssd.lock);
 229        break;
 230    case QXL_CURSOR_MOVE:
 231        qemu_mutex_lock(&qxl->ssd.lock);
 232        qxl->ssd.mouse_x = cmd->u.position.x;
 233        qxl->ssd.mouse_y = cmd->u.position.y;
 234        qemu_mutex_unlock(&qxl->ssd.lock);
 235        break;
 236    }
 237}
 238