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