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#include "trace.h"
  24
  25static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
  26{
  27    DisplaySurface *surface = qemu_console_surface(qxl->vga.con);
  28    uint8_t *dst = surface_data(surface);
  29    uint8_t *src;
  30    int len, i;
  31
  32    if (is_buffer_shared(surface)) {
  33        return;
  34    }
  35    trace_qxl_render_blit(qxl->guest_primary.qxl_stride,
  36            rect->left, rect->right, rect->top, rect->bottom);
  37    src = qxl->guest_primary.data;
  38    if (qxl->guest_primary.qxl_stride < 0) {
  39        /* qxl surface is upside down, walk src scanlines
  40         * in reverse order to flip it */
  41        src += (qxl->guest_primary.surface.height - rect->top - 1) *
  42            qxl->guest_primary.abs_stride;
  43    } else {
  44        src += rect->top * qxl->guest_primary.abs_stride;
  45    }
  46    dst += rect->top  * qxl->guest_primary.abs_stride;
  47    src += rect->left * qxl->guest_primary.bytes_pp;
  48    dst += rect->left * qxl->guest_primary.bytes_pp;
  49    len  = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
  50
  51    for (i = rect->top; i < rect->bottom; i++) {
  52        memcpy(dst, src, len);
  53        dst += qxl->guest_primary.abs_stride;
  54        src += qxl->guest_primary.qxl_stride;
  55    }
  56}
  57
  58void qxl_render_resize(PCIQXLDevice *qxl)
  59{
  60    QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
  61
  62    qxl->guest_primary.qxl_stride = sc->stride;
  63    qxl->guest_primary.abs_stride = abs(sc->stride);
  64    qxl->guest_primary.resized++;
  65    switch (sc->format) {
  66    case SPICE_SURFACE_FMT_16_555:
  67        qxl->guest_primary.bytes_pp = 2;
  68        qxl->guest_primary.bits_pp = 15;
  69        break;
  70    case SPICE_SURFACE_FMT_16_565:
  71        qxl->guest_primary.bytes_pp = 2;
  72        qxl->guest_primary.bits_pp = 16;
  73        break;
  74    case SPICE_SURFACE_FMT_32_xRGB:
  75    case SPICE_SURFACE_FMT_32_ARGB:
  76        qxl->guest_primary.bytes_pp = 4;
  77        qxl->guest_primary.bits_pp = 32;
  78        break;
  79    default:
  80        fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__,
  81                qxl->guest_primary.surface.format);
  82        qxl->guest_primary.bytes_pp = 4;
  83        qxl->guest_primary.bits_pp = 32;
  84        break;
  85    }
  86}
  87
  88static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
  89{
  90    area->left   = 0;
  91    area->right  = qxl->guest_primary.surface.width;
  92    area->top    = 0;
  93    area->bottom = qxl->guest_primary.surface.height;
  94}
  95
  96static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
  97{
  98    VGACommonState *vga = &qxl->vga;
  99    DisplaySurface *surface;
 100    int i;
 101
 102    if (qxl->guest_primary.resized) {
 103        qxl->guest_primary.resized = 0;
 104        qxl->guest_primary.data = qxl_phys2virt(qxl,
 105                                                qxl->guest_primary.surface.mem,
 106                                                MEMSLOT_GROUP_GUEST);
 107        if (!qxl->guest_primary.data) {
 108            return;
 109        }
 110        qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
 111        qxl->num_dirty_rects = 1;
 112        trace_qxl_render_guest_primary_resized(
 113               qxl->guest_primary.surface.width,
 114               qxl->guest_primary.surface.height,
 115               qxl->guest_primary.qxl_stride,
 116               qxl->guest_primary.bytes_pp,
 117               qxl->guest_primary.bits_pp);
 118        if (qxl->guest_primary.qxl_stride > 0) {
 119            pixman_format_code_t format =
 120                qemu_default_pixman_format(qxl->guest_primary.bits_pp, true);
 121            surface = qemu_create_displaysurface_from
 122                (qxl->guest_primary.surface.width,
 123                 qxl->guest_primary.surface.height,
 124                 format,
 125                 qxl->guest_primary.abs_stride,
 126                 qxl->guest_primary.data);
 127        } else {
 128            surface = qemu_create_displaysurface
 129                (qxl->guest_primary.surface.width,
 130                 qxl->guest_primary.surface.height);
 131        }
 132        dpy_gfx_replace_surface(vga->con, surface);
 133    }
 134
 135    if (!qxl->guest_primary.data) {
 136        return;
 137    }
 138    for (i = 0; i < qxl->num_dirty_rects; i++) {
 139        if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
 140            break;
 141        }
 142        if (qxl->dirty[i].left < 0 ||
 143            qxl->dirty[i].top < 0 ||
 144            qxl->dirty[i].left > qxl->dirty[i].right ||
 145            qxl->dirty[i].top > qxl->dirty[i].bottom ||
 146            qxl->dirty[i].right > qxl->guest_primary.surface.width ||
 147            qxl->dirty[i].bottom > qxl->guest_primary.surface.height) {
 148            continue;
 149        }
 150        qxl_blit(qxl, qxl->dirty+i);
 151        dpy_gfx_update(vga->con,
 152                       qxl->dirty[i].left, qxl->dirty[i].top,
 153                       qxl->dirty[i].right - qxl->dirty[i].left,
 154                       qxl->dirty[i].bottom - qxl->dirty[i].top);
 155    }
 156    qxl->num_dirty_rects = 0;
 157}
 158
 159/*
 160 * use ssd.lock to protect render_update_cookie_num.
 161 * qxl_render_update is called by io thread or vcpu thread, and the completion
 162 * callbacks are called by spice_server thread, defering to bh called from the
 163 * io thread.
 164 */
 165void qxl_render_update(PCIQXLDevice *qxl)
 166{
 167    QXLCookie *cookie;
 168
 169    qemu_mutex_lock(&qxl->ssd.lock);
 170
 171    if (!runstate_is_running() || !qxl->guest_primary.commands) {
 172        qxl_render_update_area_unlocked(qxl);
 173        qemu_mutex_unlock(&qxl->ssd.lock);
 174        return;
 175    }
 176
 177    qxl->guest_primary.commands = 0;
 178    qxl->render_update_cookie_num++;
 179    qemu_mutex_unlock(&qxl->ssd.lock);
 180    cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
 181                            0);
 182    qxl_set_rect_to_surface(qxl, &cookie->u.render.area);
 183    qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL,
 184                          0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie);
 185}
 186
 187void qxl_render_update_area_bh(void *opaque)
 188{
 189    PCIQXLDevice *qxl = opaque;
 190
 191    qemu_mutex_lock(&qxl->ssd.lock);
 192    qxl_render_update_area_unlocked(qxl);
 193    qemu_mutex_unlock(&qxl->ssd.lock);
 194}
 195
 196void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
 197{
 198    qemu_mutex_lock(&qxl->ssd.lock);
 199    trace_qxl_render_update_area_done(cookie);
 200    qemu_bh_schedule(qxl->update_area_bh);
 201    qxl->render_update_cookie_num--;
 202    qemu_mutex_unlock(&qxl->ssd.lock);
 203    g_free(cookie);
 204}
 205
 206static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
 207{
 208    QEMUCursor *c;
 209    uint8_t *image, *mask;
 210    size_t size;
 211
 212    c = cursor_alloc(cursor->header.width, cursor->header.height);
 213    c->hot_x = cursor->header.hot_spot_x;
 214    c->hot_y = cursor->header.hot_spot_y;
 215    switch (cursor->header.type) {
 216    case SPICE_CURSOR_TYPE_ALPHA:
 217        size = sizeof(uint32_t) * cursor->header.width * cursor->header.height;
 218        memcpy(c->data, cursor->chunk.data, size);
 219        if (qxl->debug > 2) {
 220            cursor_print_ascii_art(c, "qxl/alpha");
 221        }
 222        break;
 223    case SPICE_CURSOR_TYPE_MONO:
 224        mask  = cursor->chunk.data;
 225        image = mask + cursor_get_mono_bpl(c) * c->width;
 226        cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask);
 227        if (qxl->debug > 2) {
 228            cursor_print_ascii_art(c, "qxl/mono");
 229        }
 230        break;
 231    default:
 232        fprintf(stderr, "%s: not implemented: type %d\n",
 233                __FUNCTION__, cursor->header.type);
 234        goto fail;
 235    }
 236    return c;
 237
 238fail:
 239    cursor_put(c);
 240    return NULL;
 241}
 242
 243
 244/* called from spice server thread context only */
 245int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
 246{
 247    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
 248    QXLCursor *cursor;
 249    QEMUCursor *c;
 250
 251    if (!cmd) {
 252        return 1;
 253    }
 254
 255    if (!dpy_cursor_define_supported(qxl->vga.con)) {
 256        return 0;
 257    }
 258
 259    if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) {
 260        fprintf(stderr, "%s", __FUNCTION__);
 261        qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
 262        fprintf(stderr, "\n");
 263    }
 264    switch (cmd->type) {
 265    case QXL_CURSOR_SET:
 266        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
 267        if (!cursor) {
 268            return 1;
 269        }
 270        if (cursor->chunk.data_size != cursor->data_size) {
 271            fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__);
 272            return 1;
 273        }
 274        c = qxl_cursor(qxl, cursor);
 275        if (c == NULL) {
 276            c = cursor_builtin_left_ptr();
 277        }
 278        qemu_mutex_lock(&qxl->ssd.lock);
 279        if (qxl->ssd.cursor) {
 280            cursor_put(qxl->ssd.cursor);
 281        }
 282        qxl->ssd.cursor = c;
 283        qxl->ssd.mouse_x = cmd->u.set.position.x;
 284        qxl->ssd.mouse_y = cmd->u.set.position.y;
 285        qemu_mutex_unlock(&qxl->ssd.lock);
 286        break;
 287    case QXL_CURSOR_MOVE:
 288        qemu_mutex_lock(&qxl->ssd.lock);
 289        qxl->ssd.mouse_x = cmd->u.position.x;
 290        qxl->ssd.mouse_y = cmd->u.position.y;
 291        qemu_mutex_unlock(&qxl->ssd.lock);
 292        break;
 293    }
 294    return 0;
 295}
 296