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/>.
24 static void qxl_blit(PCIQXLDevice
*qxl
, QXLRect
*rect
)
27 uint8_t *dst
= qxl
->vga
.ds
->surface
->data
;
30 if (is_buffer_shared(qxl
->vga
.ds
->surface
)) {
33 if (!qxl
->guest_primary
.data
) {
34 dprint(qxl
, 1, "%s: initializing guest_primary.data\n", __func__
);
35 qxl
->guest_primary
.data
= memory_region_get_ram_ptr(&qxl
->vga
.vram
);
37 dprint(qxl
, 2, "%s: stride %d, [%d, %d, %d, %d]\n", __func__
,
38 qxl
->guest_primary
.qxl_stride
,
39 rect
->left
, rect
->right
, rect
->top
, rect
->bottom
);
40 src
= qxl
->guest_primary
.data
;
41 if (qxl
->guest_primary
.qxl_stride
< 0) {
42 /* qxl surface is upside down, walk src scanlines
43 * in reverse order to flip it */
44 src
+= (qxl
->guest_primary
.surface
.height
- rect
->top
- 1) *
45 qxl
->guest_primary
.abs_stride
;
47 src
+= rect
->top
* qxl
->guest_primary
.abs_stride
;
49 dst
+= rect
->top
* qxl
->guest_primary
.abs_stride
;
50 src
+= rect
->left
* qxl
->guest_primary
.bytes_pp
;
51 dst
+= rect
->left
* qxl
->guest_primary
.bytes_pp
;
52 len
= (rect
->right
- rect
->left
) * qxl
->guest_primary
.bytes_pp
;
54 for (i
= rect
->top
; i
< rect
->bottom
; i
++) {
55 memcpy(dst
, src
, len
);
56 dst
+= qxl
->guest_primary
.abs_stride
;
57 src
+= qxl
->guest_primary
.qxl_stride
;
61 void qxl_render_resize(PCIQXLDevice
*qxl
)
63 QXLSurfaceCreate
*sc
= &qxl
->guest_primary
.surface
;
65 qxl
->guest_primary
.qxl_stride
= sc
->stride
;
66 qxl
->guest_primary
.abs_stride
= abs(sc
->stride
);
67 qxl
->guest_primary
.resized
++;
69 case SPICE_SURFACE_FMT_16_555
:
70 qxl
->guest_primary
.bytes_pp
= 2;
71 qxl
->guest_primary
.bits_pp
= 15;
73 case SPICE_SURFACE_FMT_16_565
:
74 qxl
->guest_primary
.bytes_pp
= 2;
75 qxl
->guest_primary
.bits_pp
= 16;
77 case SPICE_SURFACE_FMT_32_xRGB
:
78 case SPICE_SURFACE_FMT_32_ARGB
:
79 qxl
->guest_primary
.bytes_pp
= 4;
80 qxl
->guest_primary
.bits_pp
= 32;
83 fprintf(stderr
, "%s: unhandled format: %x\n", __FUNCTION__
,
84 qxl
->guest_primary
.surface
.format
);
85 qxl
->guest_primary
.bytes_pp
= 4;
86 qxl
->guest_primary
.bits_pp
= 32;
91 static void qxl_set_rect_to_surface(PCIQXLDevice
*qxl
, QXLRect
*area
)
94 area
->right
= qxl
->guest_primary
.surface
.width
;
96 area
->bottom
= qxl
->guest_primary
.surface
.height
;
99 static void qxl_render_update_area_unlocked(PCIQXLDevice
*qxl
)
101 VGACommonState
*vga
= &qxl
->vga
;
103 DisplaySurface
*surface
= vga
->ds
->surface
;
105 if (qxl
->guest_primary
.resized
) {
106 qxl
->guest_primary
.resized
= 0;
107 qxl
->guest_primary
.data
= memory_region_get_ram_ptr(&qxl
->vga
.vram
);
108 qxl_set_rect_to_surface(qxl
, &qxl
->dirty
[0]);
109 qxl
->num_dirty_rects
= 1;
110 dprint(qxl
, 1, "%s: %dx%d, stride %d, bpp %d, depth %d\n",
112 qxl
->guest_primary
.surface
.width
,
113 qxl
->guest_primary
.surface
.height
,
114 qxl
->guest_primary
.qxl_stride
,
115 qxl
->guest_primary
.bytes_pp
,
116 qxl
->guest_primary
.bits_pp
);
118 if (surface
->width
!= qxl
->guest_primary
.surface
.width
||
119 surface
->height
!= qxl
->guest_primary
.surface
.height
) {
120 if (qxl
->guest_primary
.qxl_stride
> 0) {
121 dprint(qxl
, 1, "%s: using guest_primary for displaysurface\n",
123 qemu_free_displaysurface(vga
->ds
);
124 qemu_create_displaysurface_from(qxl
->guest_primary
.surface
.width
,
125 qxl
->guest_primary
.surface
.height
,
126 qxl
->guest_primary
.bits_pp
,
127 qxl
->guest_primary
.abs_stride
,
128 qxl
->guest_primary
.data
);
130 dprint(qxl
, 1, "%s: resizing displaysurface to guest_primary\n",
132 qemu_resize_displaysurface(vga
->ds
,
133 qxl
->guest_primary
.surface
.width
,
134 qxl
->guest_primary
.surface
.height
);
137 for (i
= 0; i
< qxl
->num_dirty_rects
; i
++) {
138 if (qemu_spice_rect_is_empty(qxl
->dirty
+i
)) {
141 qxl_blit(qxl
, qxl
->dirty
+i
);
143 qxl
->dirty
[i
].left
, qxl
->dirty
[i
].top
,
144 qxl
->dirty
[i
].right
- qxl
->dirty
[i
].left
,
145 qxl
->dirty
[i
].bottom
- qxl
->dirty
[i
].top
);
147 qxl
->num_dirty_rects
= 0;
151 * use ssd.lock to protect render_update_cookie_num.
152 * qxl_render_update is called by io thread or vcpu thread, and the completion
153 * callbacks are called by spice_server thread, defering to bh called from the
156 void qxl_render_update(PCIQXLDevice
*qxl
)
160 qemu_mutex_lock(&qxl
->ssd
.lock
);
162 if (!runstate_is_running() || !qxl
->guest_primary
.commands
) {
163 qxl_render_update_area_unlocked(qxl
);
164 qemu_mutex_unlock(&qxl
->ssd
.lock
);
168 qxl
->guest_primary
.commands
= 0;
169 qxl
->render_update_cookie_num
++;
170 qemu_mutex_unlock(&qxl
->ssd
.lock
);
171 cookie
= qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA
,
173 qxl_set_rect_to_surface(qxl
, &cookie
->u
.render
.area
);
174 qxl_spice_update_area(qxl
, 0, &cookie
->u
.render
.area
, NULL
,
175 0, 1 /* clear_dirty_region */, QXL_ASYNC
, cookie
);
178 void qxl_render_update_area_bh(void *opaque
)
180 PCIQXLDevice
*qxl
= opaque
;
182 qemu_mutex_lock(&qxl
->ssd
.lock
);
183 qxl_render_update_area_unlocked(qxl
);
184 qemu_mutex_unlock(&qxl
->ssd
.lock
);
187 void qxl_render_update_area_done(PCIQXLDevice
*qxl
, QXLCookie
*cookie
)
189 qemu_mutex_lock(&qxl
->ssd
.lock
);
190 qemu_bh_schedule(qxl
->update_area_bh
);
191 qxl
->render_update_cookie_num
--;
192 qemu_mutex_unlock(&qxl
->ssd
.lock
);
196 static QEMUCursor
*qxl_cursor(PCIQXLDevice
*qxl
, QXLCursor
*cursor
)
199 uint8_t *image
, *mask
;
202 c
= cursor_alloc(cursor
->header
.width
, cursor
->header
.height
);
203 c
->hot_x
= cursor
->header
.hot_spot_x
;
204 c
->hot_y
= cursor
->header
.hot_spot_y
;
205 switch (cursor
->header
.type
) {
206 case SPICE_CURSOR_TYPE_ALPHA
:
207 size
= cursor
->header
.width
* cursor
->header
.height
* sizeof(uint32_t);
208 memcpy(c
->data
, cursor
->chunk
.data
, size
);
209 if (qxl
->debug
> 2) {
210 cursor_print_ascii_art(c
, "qxl/alpha");
213 case SPICE_CURSOR_TYPE_MONO
:
214 mask
= cursor
->chunk
.data
;
215 image
= mask
+ cursor_get_mono_bpl(c
) * c
->width
;
216 cursor_set_mono(c
, 0xffffff, 0x000000, image
, 1, mask
);
217 if (qxl
->debug
> 2) {
218 cursor_print_ascii_art(c
, "qxl/mono");
222 fprintf(stderr
, "%s: not implemented: type %d\n",
223 __FUNCTION__
, cursor
->header
.type
);
234 /* called from spice server thread context only */
235 void qxl_render_cursor(PCIQXLDevice
*qxl
, QXLCommandExt
*ext
)
237 QXLCursorCmd
*cmd
= qxl_phys2virt(qxl
, ext
->cmd
.data
, ext
->group_id
);
241 if (!qxl
->ssd
.ds
->mouse_set
|| !qxl
->ssd
.ds
->cursor_define
) {
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");
252 cursor
= qxl_phys2virt(qxl
, cmd
->u
.set
.shape
, ext
->group_id
);
253 if (cursor
->chunk
.data_size
!= cursor
->data_size
) {
254 fprintf(stderr
, "%s: multiple chunks\n", __FUNCTION__
);
257 c
= qxl_cursor(qxl
, cursor
);
259 c
= cursor_builtin_left_ptr();
261 qemu_mutex_lock(&qxl
->ssd
.lock
);
262 if (qxl
->ssd
.cursor
) {
263 cursor_put(qxl
->ssd
.cursor
);
266 qxl
->ssd
.mouse_x
= cmd
->u
.set
.position
.x
;
267 qxl
->ssd
.mouse_y
= cmd
->u
.set
.position
.y
;
268 qemu_mutex_unlock(&qxl
->ssd
.lock
);
270 case QXL_CURSOR_MOVE
:
271 qemu_mutex_lock(&qxl
->ssd
.lock
);
272 qxl
->ssd
.mouse_x
= cmd
->u
.position
.x
;
273 qxl
->ssd
.mouse_y
= cmd
->u
.position
.y
;
274 qemu_mutex_unlock(&qxl
->ssd
.lock
);