2 * qxl local rendering (aka display on sdl/vnc)
4 * Copyright (C) 2010 Red Hat, Inc.
6 * maintained by Gerd Hoffmann <kraxel@redhat.com>
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.
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.
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/>.
22 #include "qemu/osdep.h"
24 #include "sysemu/runstate.h"
27 static void qxl_blit(PCIQXLDevice
*qxl
, QXLRect
*rect
)
29 DisplaySurface
*surface
= qemu_console_surface(qxl
->vga
.con
);
30 uint8_t *dst
= surface_data(surface
);
34 if (is_buffer_shared(surface
)) {
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
;
46 src
+= rect
->top
* qxl
->guest_primary
.abs_stride
;
48 dst
+= rect
->top
* qxl
->guest_primary
.abs_stride
;
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
;
53 for (i
= rect
->top
; i
< rect
->bottom
; i
++) {
54 memcpy(dst
, src
, len
);
55 dst
+= qxl
->guest_primary
.abs_stride
;
56 src
+= qxl
->guest_primary
.qxl_stride
;
60 void qxl_render_resize(PCIQXLDevice
*qxl
)
62 QXLSurfaceCreate
*sc
= &qxl
->guest_primary
.surface
;
64 qxl
->guest_primary
.qxl_stride
= sc
->stride
;
65 qxl
->guest_primary
.abs_stride
= abs(sc
->stride
);
66 qxl
->guest_primary
.resized
++;
68 case SPICE_SURFACE_FMT_16_555
:
69 qxl
->guest_primary
.bytes_pp
= 2;
70 qxl
->guest_primary
.bits_pp
= 15;
72 case SPICE_SURFACE_FMT_16_565
:
73 qxl
->guest_primary
.bytes_pp
= 2;
74 qxl
->guest_primary
.bits_pp
= 16;
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;
82 fprintf(stderr
, "%s: unhandled format: %x\n", __func__
,
83 qxl
->guest_primary
.surface
.format
);
84 qxl
->guest_primary
.bytes_pp
= 4;
85 qxl
->guest_primary
.bits_pp
= 32;
90 static void qxl_set_rect_to_surface(PCIQXLDevice
*qxl
, QXLRect
*area
)
93 area
->right
= qxl
->guest_primary
.surface
.width
;
95 area
->bottom
= qxl
->guest_primary
.surface
.height
;
98 static void qxl_render_update_area_unlocked(PCIQXLDevice
*qxl
)
100 VGACommonState
*vga
= &qxl
->vga
;
101 DisplaySurface
*surface
;
102 int width
= qxl
->guest_head0_width
?: qxl
->guest_primary
.surface
.width
;
103 int height
= qxl
->guest_head0_height
?: qxl
->guest_primary
.surface
.height
;
106 if (qxl
->guest_primary
.resized
) {
107 qxl
->guest_primary
.resized
= 0;
108 qxl
->guest_primary
.data
= qxl_phys2virt(qxl
,
109 qxl
->guest_primary
.surface
.mem
,
111 qxl
->guest_primary
.abs_stride
113 if (!qxl
->guest_primary
.data
) {
116 qxl_set_rect_to_surface(qxl
, &qxl
->dirty
[0]);
117 qxl
->num_dirty_rects
= 1;
118 trace_qxl_render_guest_primary_resized(
121 qxl
->guest_primary
.qxl_stride
,
122 qxl
->guest_primary
.bytes_pp
,
123 qxl
->guest_primary
.bits_pp
);
124 if (qxl
->guest_primary
.qxl_stride
> 0) {
125 pixman_format_code_t format
=
126 qemu_default_pixman_format(qxl
->guest_primary
.bits_pp
, true);
127 surface
= qemu_create_displaysurface_from
131 qxl
->guest_primary
.abs_stride
,
132 qxl
->guest_primary
.data
);
134 surface
= qemu_create_displaysurface
138 dpy_gfx_replace_surface(vga
->con
, surface
);
141 if (!qxl
->guest_primary
.data
) {
144 for (i
= 0; i
< qxl
->num_dirty_rects
; i
++) {
145 if (qemu_spice_rect_is_empty(qxl
->dirty
+i
)) {
148 if (qxl
->dirty
[i
].left
< 0 ||
149 qxl
->dirty
[i
].top
< 0 ||
150 qxl
->dirty
[i
].left
> qxl
->dirty
[i
].right
||
151 qxl
->dirty
[i
].top
> qxl
->dirty
[i
].bottom
||
152 qxl
->dirty
[i
].right
> width
||
153 qxl
->dirty
[i
].bottom
> height
) {
156 qxl_blit(qxl
, qxl
->dirty
+i
);
157 dpy_gfx_update(vga
->con
,
158 qxl
->dirty
[i
].left
, qxl
->dirty
[i
].top
,
159 qxl
->dirty
[i
].right
- qxl
->dirty
[i
].left
,
160 qxl
->dirty
[i
].bottom
- qxl
->dirty
[i
].top
);
162 qxl
->num_dirty_rects
= 0;
165 if (qxl
->render_update_cookie_num
== 0) {
166 graphic_hw_update_done(qxl
->ssd
.dcl
.con
);
171 * use ssd.lock to protect render_update_cookie_num.
172 * qxl_render_update is called by io thread or vcpu thread, and the completion
173 * callbacks are called by spice_server thread, deferring to bh called from the
176 void qxl_render_update(PCIQXLDevice
*qxl
)
180 qemu_mutex_lock(&qxl
->ssd
.lock
);
182 if (!runstate_is_running() || !qxl
->guest_primary
.commands
||
183 qxl
->mode
== QXL_MODE_UNDEFINED
) {
184 qxl_render_update_area_unlocked(qxl
);
185 qemu_mutex_unlock(&qxl
->ssd
.lock
);
186 graphic_hw_update_done(qxl
->ssd
.dcl
.con
);
190 qxl
->guest_primary
.commands
= 0;
191 qxl
->render_update_cookie_num
++;
192 qemu_mutex_unlock(&qxl
->ssd
.lock
);
193 cookie
= qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA
,
195 qxl_set_rect_to_surface(qxl
, &cookie
->u
.render
.area
);
196 qxl_spice_update_area(qxl
, 0, &cookie
->u
.render
.area
, NULL
,
197 0, 1 /* clear_dirty_region */, QXL_ASYNC
, cookie
);
200 void qxl_render_update_area_bh(void *opaque
)
202 PCIQXLDevice
*qxl
= opaque
;
204 qemu_mutex_lock(&qxl
->ssd
.lock
);
205 qxl_render_update_area_unlocked(qxl
);
206 qemu_mutex_unlock(&qxl
->ssd
.lock
);
209 void qxl_render_update_area_done(PCIQXLDevice
*qxl
, QXLCookie
*cookie
)
211 qemu_mutex_lock(&qxl
->ssd
.lock
);
212 trace_qxl_render_update_area_done(cookie
);
213 qemu_bh_schedule(qxl
->update_area_bh
);
214 qxl
->render_update_cookie_num
--;
215 qemu_mutex_unlock(&qxl
->ssd
.lock
);
219 static void qxl_unpack_chunks(void *dest
, size_t size
, PCIQXLDevice
*qxl
,
220 QXLDataChunk
*chunk
, uint32_t group_id
)
222 uint32_t max_chunks
= 32;
227 bytes
= MIN(size
- offset
, chunk
->data_size
);
228 memcpy(dest
+ offset
, chunk
->data
, bytes
);
230 if (offset
== size
) {
233 chunk
= qxl_phys2virt(qxl
, chunk
->next_chunk
, group_id
,
234 sizeof(QXLDataChunk
) + chunk
->data_size
);
239 if (max_chunks
== 0) {
245 static QEMUCursor
*qxl_cursor(PCIQXLDevice
*qxl
, QXLCursor
*cursor
,
249 uint8_t *and_mask
, *xor_mask
;
252 c
= cursor_alloc(cursor
->header
.width
, cursor
->header
.height
);
255 qxl_set_guest_bug(qxl
, "%s: cursor %ux%u alloc error", __func__
,
256 cursor
->header
.width
, cursor
->header
.height
);
260 c
->hot_x
= cursor
->header
.hot_spot_x
;
261 c
->hot_y
= cursor
->header
.hot_spot_y
;
262 switch (cursor
->header
.type
) {
263 case SPICE_CURSOR_TYPE_MONO
:
264 /* Assume that the full cursor is available in a single chunk. */
265 size
= 2 * cursor_get_mono_bpl(c
) * c
->height
;
266 if (size
!= cursor
->data_size
) {
267 fprintf(stderr
, "%s: bad monochrome cursor %ux%u with size %u\n",
268 __func__
, c
->width
, c
->height
, cursor
->data_size
);
271 and_mask
= cursor
->chunk
.data
;
272 xor_mask
= and_mask
+ cursor_get_mono_bpl(c
) * c
->height
;
273 cursor_set_mono(c
, 0xffffff, 0x000000, xor_mask
, 1, and_mask
);
274 if (qxl
->debug
> 2) {
275 cursor_print_ascii_art(c
, "qxl/mono");
278 case SPICE_CURSOR_TYPE_ALPHA
:
279 size
= sizeof(uint32_t) * c
->width
* c
->height
;
280 qxl_unpack_chunks(c
->data
, size
, qxl
, &cursor
->chunk
, group_id
);
281 if (qxl
->debug
> 2) {
282 cursor_print_ascii_art(c
, "qxl/alpha");
286 fprintf(stderr
, "%s: not implemented: type %d\n",
287 __func__
, cursor
->header
.type
);
298 /* called from spice server thread context only */
299 int qxl_render_cursor(PCIQXLDevice
*qxl
, QXLCommandExt
*ext
)
301 QXLCursorCmd
*cmd
= qxl_phys2virt(qxl
, ext
->cmd
.data
, ext
->group_id
,
302 sizeof(QXLCursorCmd
));
310 if (!dpy_cursor_define_supported(qxl
->vga
.con
)) {
314 if (qxl
->debug
> 1 && cmd
->type
!= QXL_CURSOR_MOVE
) {
315 fprintf(stderr
, "%s", __func__
);
316 qxl_log_cmd_cursor(qxl
, cmd
, ext
->group_id
);
317 fprintf(stderr
, "\n");
321 /* First read the QXLCursor to get QXLDataChunk::data_size ... */
322 cursor
= qxl_phys2virt(qxl
, cmd
->u
.set
.shape
, ext
->group_id
,
327 /* Then read including the chunked data following QXLCursor. */
328 cursor
= qxl_phys2virt(qxl
, cmd
->u
.set
.shape
, ext
->group_id
,
329 sizeof(QXLCursor
) + cursor
->chunk
.data_size
);
333 c
= qxl_cursor(qxl
, cursor
, ext
->group_id
);
335 c
= cursor_builtin_left_ptr();
337 qemu_mutex_lock(&qxl
->ssd
.lock
);
338 if (qxl
->ssd
.cursor
) {
339 cursor_unref(qxl
->ssd
.cursor
);
342 qxl
->ssd
.mouse_x
= cmd
->u
.set
.position
.x
;
343 qxl
->ssd
.mouse_y
= cmd
->u
.set
.position
.y
;
344 qemu_mutex_unlock(&qxl
->ssd
.lock
);
345 qemu_bh_schedule(qxl
->ssd
.cursor_bh
);
347 case QXL_CURSOR_MOVE
:
348 qemu_mutex_lock(&qxl
->ssd
.lock
);
349 qxl
->ssd
.mouse_x
= cmd
->u
.position
.x
;
350 qxl
->ssd
.mouse_y
= cmd
->u
.position
.y
;
351 qemu_mutex_unlock(&qxl
->ssd
.lock
);
352 qemu_bh_schedule(qxl
->ssd
.cursor_bh
);