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