1 #include "qemu/osdep.h"
2 #include "qapi/error.h"
3 #include "include/qemu-common.h"
4 #include "chardev/char.h"
5 #include "qemu/buffer.h"
6 #include "qemu/option.h"
7 #include "qemu/units.h"
8 #include "hw/qdev-core.h"
9 #include "ui/clipboard.h"
10 #include "ui/console.h"
14 #include "qapi/qapi-types-char.h"
15 #include "qapi/qapi-types-ui.h"
17 #include "spice/vd_agent.h"
19 #define VDAGENT_BUFFER_LIMIT (1 * MiB)
20 #define VDAGENT_MOUSE_DEFAULT true
21 #define VDAGENT_CLIPBOARD_DEFAULT false
23 struct VDAgentChardev
{
41 DeviceState mouse_dev
;
45 uint32_t mouse_display
;
46 QemuInputHandlerState
*mouse_hs
;
49 QemuClipboardPeer cbpeer
;
50 QemuClipboardInfo
*cbinfo
[QEMU_CLIPBOARD_SELECTION__COUNT
];
51 uint32_t cbpending
[QEMU_CLIPBOARD_SELECTION__COUNT
];
53 typedef struct VDAgentChardev VDAgentChardev
;
55 #define TYPE_CHARDEV_QEMU_VDAGENT "chardev-qemu-vdagent"
57 DECLARE_INSTANCE_CHECKER(VDAgentChardev
, QEMU_VDAGENT_CHARDEV
,
58 TYPE_CHARDEV_QEMU_VDAGENT
);
60 /* ------------------------------------------------------------------ */
61 /* names, for debug logging */
63 static const char *cap_name
[] = {
64 [VD_AGENT_CAP_MOUSE_STATE
] = "mouse-state",
65 [VD_AGENT_CAP_MONITORS_CONFIG
] = "monitors-config",
66 [VD_AGENT_CAP_REPLY
] = "reply",
67 [VD_AGENT_CAP_CLIPBOARD
] = "clipboard",
68 [VD_AGENT_CAP_DISPLAY_CONFIG
] = "display-config",
69 [VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
] = "clipboard-by-demand",
70 [VD_AGENT_CAP_CLIPBOARD_SELECTION
] = "clipboard-selection",
71 [VD_AGENT_CAP_SPARSE_MONITORS_CONFIG
] = "sparse-monitors-config",
72 [VD_AGENT_CAP_GUEST_LINEEND_LF
] = "guest-lineend-lf",
73 [VD_AGENT_CAP_GUEST_LINEEND_CRLF
] = "guest-lineend-crlf",
74 [VD_AGENT_CAP_MAX_CLIPBOARD
] = "max-clipboard",
75 [VD_AGENT_CAP_AUDIO_VOLUME_SYNC
] = "audio-volume-sync",
76 [VD_AGENT_CAP_MONITORS_CONFIG_POSITION
] = "monitors-config-position",
77 [VD_AGENT_CAP_FILE_XFER_DISABLED
] = "file-xfer-disabled",
78 [VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS
] = "file-xfer-detailed-errors",
80 [VD_AGENT_CAP_GRAPHICS_DEVICE_INFO
] = "graphics-device-info",
81 [VD_AGENT_CAP_CLIPBOARD_NO_RELEASE_ON_REGRAB
] = "clipboard-no-release-on-regrab",
82 [VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL
] = "clipboard-grab-serial",
86 static const char *msg_name
[] = {
87 [VD_AGENT_MOUSE_STATE
] = "mouse-state",
88 [VD_AGENT_MONITORS_CONFIG
] = "monitors-config",
89 [VD_AGENT_REPLY
] = "reply",
90 [VD_AGENT_CLIPBOARD
] = "clipboard",
91 [VD_AGENT_DISPLAY_CONFIG
] = "display-config",
92 [VD_AGENT_ANNOUNCE_CAPABILITIES
] = "announce-capabilities",
93 [VD_AGENT_CLIPBOARD_GRAB
] = "clipboard-grab",
94 [VD_AGENT_CLIPBOARD_REQUEST
] = "clipboard-request",
95 [VD_AGENT_CLIPBOARD_RELEASE
] = "clipboard-release",
96 [VD_AGENT_FILE_XFER_START
] = "file-xfer-start",
97 [VD_AGENT_FILE_XFER_STATUS
] = "file-xfer-status",
98 [VD_AGENT_FILE_XFER_DATA
] = "file-xfer-data",
99 [VD_AGENT_CLIENT_DISCONNECTED
] = "client-disconnected",
100 [VD_AGENT_MAX_CLIPBOARD
] = "max-clipboard",
101 [VD_AGENT_AUDIO_VOLUME_SYNC
] = "audio-volume-sync",
103 [VD_AGENT_GRAPHICS_DEVICE_INFO
] = "graphics-device-info",
107 static const char *sel_name
[] = {
108 [VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
] = "clipboard",
109 [VD_AGENT_CLIPBOARD_SELECTION_PRIMARY
] = "primary",
110 [VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
] = "secondary",
113 static const char *type_name
[] = {
114 [VD_AGENT_CLIPBOARD_NONE
] = "none",
115 [VD_AGENT_CLIPBOARD_UTF8_TEXT
] = "text",
116 [VD_AGENT_CLIPBOARD_IMAGE_PNG
] = "png",
117 [VD_AGENT_CLIPBOARD_IMAGE_BMP
] = "bmp",
118 [VD_AGENT_CLIPBOARD_IMAGE_TIFF
] = "tiff",
119 [VD_AGENT_CLIPBOARD_IMAGE_JPG
] = "jpg",
121 [VD_AGENT_CLIPBOARD_FILE_LIST
] = "files",
125 #define GET_NAME(_m, _v) \
126 (((_v) < ARRAY_SIZE(_m) && (_m[_v])) ? (_m[_v]) : "???")
128 /* ------------------------------------------------------------------ */
131 static void vdagent_send_buf(VDAgentChardev
*vd
)
135 while (!buffer_empty(&vd
->outbuf
)) {
136 len
= qemu_chr_be_can_write(CHARDEV(vd
));
140 if (len
> vd
->outbuf
.offset
) {
141 len
= vd
->outbuf
.offset
;
143 qemu_chr_be_write(CHARDEV(vd
), vd
->outbuf
.buffer
, len
);
144 buffer_advance(&vd
->outbuf
, len
);
148 static void vdagent_send_msg(VDAgentChardev
*vd
, VDAgentMessage
*msg
)
150 uint8_t *msgbuf
= (void *)msg
;
151 uint32_t msgsize
= sizeof(VDAgentMessage
) + msg
->size
;
153 VDIChunkHeader chunk
;
155 trace_vdagent_send(GET_NAME(msg_name
, msg
->type
));
157 msg
->protocol
= VD_AGENT_PROTOCOL
;
159 if (vd
->outbuf
.offset
+ msgsize
> VDAGENT_BUFFER_LIMIT
) {
160 error_report("buffer full, dropping message");
164 while (msgoff
< msgsize
) {
165 chunk
.port
= VDP_CLIENT_PORT
;
166 chunk
.size
= msgsize
- msgoff
;
167 if (chunk
.size
> 1024) {
170 buffer_reserve(&vd
->outbuf
, sizeof(chunk
) + chunk
.size
);
171 buffer_append(&vd
->outbuf
, &chunk
, sizeof(chunk
));
172 buffer_append(&vd
->outbuf
, msgbuf
+ msgoff
, chunk
.size
);
173 msgoff
+= chunk
.size
;
175 vdagent_send_buf(vd
);
178 static void vdagent_send_caps(VDAgentChardev
*vd
)
180 g_autofree VDAgentMessage
*msg
= g_malloc0(sizeof(VDAgentMessage
) +
181 sizeof(VDAgentAnnounceCapabilities
) +
183 VDAgentAnnounceCapabilities
*caps
= (void *)msg
->data
;
185 msg
->type
= VD_AGENT_ANNOUNCE_CAPABILITIES
;
186 msg
->size
= sizeof(VDAgentAnnounceCapabilities
) + sizeof(uint32_t);
188 caps
->caps
[0] |= (1 << VD_AGENT_CAP_MOUSE_STATE
);
191 caps
->caps
[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
192 caps
->caps
[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_SELECTION
);
195 vdagent_send_msg(vd
, msg
);
198 /* ------------------------------------------------------------------ */
201 static bool have_mouse(VDAgentChardev
*vd
)
204 (vd
->caps
& (1 << VD_AGENT_CAP_MOUSE_STATE
));
207 static void vdagent_send_mouse(VDAgentChardev
*vd
)
209 g_autofree VDAgentMessage
*msg
= g_malloc0(sizeof(VDAgentMessage
) +
210 sizeof(VDAgentMouseState
));
211 VDAgentMouseState
*mouse
= (void *)msg
->data
;
213 msg
->type
= VD_AGENT_MOUSE_STATE
;
214 msg
->size
= sizeof(VDAgentMouseState
);
216 mouse
->x
= vd
->mouse_x
;
217 mouse
->y
= vd
->mouse_y
;
218 mouse
->buttons
= vd
->mouse_btn
;
219 mouse
->display_id
= vd
->mouse_display
;
221 vdagent_send_msg(vd
, msg
);
224 static void vdagent_pointer_event(DeviceState
*dev
, QemuConsole
*src
,
227 static const int bmap
[INPUT_BUTTON__MAX
] = {
228 [INPUT_BUTTON_LEFT
] = VD_AGENT_LBUTTON_MASK
,
229 [INPUT_BUTTON_RIGHT
] = VD_AGENT_RBUTTON_MASK
,
230 [INPUT_BUTTON_MIDDLE
] = VD_AGENT_MBUTTON_MASK
,
231 [INPUT_BUTTON_WHEEL_UP
] = VD_AGENT_UBUTTON_MASK
,
232 [INPUT_BUTTON_WHEEL_DOWN
] = VD_AGENT_DBUTTON_MASK
,
233 #ifdef VD_AGENT_EBUTTON_MASK
234 [INPUT_BUTTON_SIDE
] = VD_AGENT_SBUTTON_MASK
,
235 [INPUT_BUTTON_EXTRA
] = VD_AGENT_EBUTTON_MASK
,
239 VDAgentChardev
*vd
= container_of(dev
, struct VDAgentChardev
, mouse_dev
);
240 InputMoveEvent
*move
;
245 case INPUT_EVENT_KIND_ABS
:
246 move
= evt
->u
.abs
.data
;
247 xres
= qemu_console_get_width(src
, 1024);
248 yres
= qemu_console_get_height(src
, 768);
249 if (move
->axis
== INPUT_AXIS_X
) {
250 vd
->mouse_x
= qemu_input_scale_axis(move
->value
,
254 } else if (move
->axis
== INPUT_AXIS_Y
) {
255 vd
->mouse_y
= qemu_input_scale_axis(move
->value
,
260 vd
->mouse_display
= qemu_console_get_index(src
);
263 case INPUT_EVENT_KIND_BTN
:
264 btn
= evt
->u
.btn
.data
;
266 vd
->mouse_btn
|= bmap
[btn
->button
];
268 vd
->mouse_btn
&= ~bmap
[btn
->button
];
278 static void vdagent_pointer_sync(DeviceState
*dev
)
280 VDAgentChardev
*vd
= container_of(dev
, struct VDAgentChardev
, mouse_dev
);
282 if (vd
->caps
& (1 << VD_AGENT_CAP_MOUSE_STATE
)) {
283 vdagent_send_mouse(vd
);
287 static QemuInputHandler vdagent_mouse_handler
= {
288 .name
= "vdagent mouse",
289 .mask
= INPUT_EVENT_MASK_BTN
| INPUT_EVENT_MASK_ABS
,
290 .event
= vdagent_pointer_event
,
291 .sync
= vdagent_pointer_sync
,
294 /* ------------------------------------------------------------------ */
297 static bool have_clipboard(VDAgentChardev
*vd
)
299 return vd
->clipboard
&&
300 (vd
->caps
& (1 << VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
));
303 static bool have_selection(VDAgentChardev
*vd
)
305 return vd
->caps
& (1 << VD_AGENT_CAP_CLIPBOARD_SELECTION
);
308 static uint32_t type_qemu_to_vdagent(enum QemuClipboardType type
)
311 case QEMU_CLIPBOARD_TYPE_TEXT
:
312 return VD_AGENT_CLIPBOARD_UTF8_TEXT
;
314 return VD_AGENT_CLIPBOARD_NONE
;
318 static void vdagent_send_clipboard_grab(VDAgentChardev
*vd
,
319 QemuClipboardInfo
*info
)
321 g_autofree VDAgentMessage
*msg
=
322 g_malloc0(sizeof(VDAgentMessage
) +
323 sizeof(uint32_t) * (QEMU_CLIPBOARD_TYPE__COUNT
+ 1));
324 uint8_t *s
= msg
->data
;
325 uint32_t *data
= (uint32_t *)msg
->data
;
328 if (have_selection(vd
)) {
329 *s
= info
->selection
;
331 msg
->size
+= sizeof(uint32_t);
332 } else if (info
->selection
!= QEMU_CLIPBOARD_SELECTION_CLIPBOARD
) {
336 for (q
= 0; q
< QEMU_CLIPBOARD_TYPE__COUNT
; q
++) {
337 type
= type_qemu_to_vdagent(q
);
338 if (type
!= VD_AGENT_CLIPBOARD_NONE
&& info
->types
[q
].available
) {
341 msg
->size
+= sizeof(uint32_t);
345 msg
->type
= VD_AGENT_CLIPBOARD_GRAB
;
346 vdagent_send_msg(vd
, msg
);
349 static void vdagent_send_clipboard_data(VDAgentChardev
*vd
,
350 QemuClipboardInfo
*info
,
351 QemuClipboardType type
)
353 g_autofree VDAgentMessage
*msg
= g_malloc0(sizeof(VDAgentMessage
) +
354 sizeof(uint32_t) * 2 +
355 info
->types
[type
].size
);
357 uint8_t *s
= msg
->data
;
358 uint32_t *data
= (uint32_t *)msg
->data
;
360 if (have_selection(vd
)) {
361 *s
= info
->selection
;
363 msg
->size
+= sizeof(uint32_t);
364 } else if (info
->selection
!= QEMU_CLIPBOARD_SELECTION_CLIPBOARD
) {
368 *data
= type_qemu_to_vdagent(type
);
370 msg
->size
+= sizeof(uint32_t);
372 memcpy(data
, info
->types
[type
].data
, info
->types
[type
].size
);
373 msg
->size
+= info
->types
[type
].size
;
375 msg
->type
= VD_AGENT_CLIPBOARD
;
376 vdagent_send_msg(vd
, msg
);
379 static void vdagent_clipboard_notify(Notifier
*notifier
, void *data
)
381 VDAgentChardev
*vd
= container_of(notifier
, VDAgentChardev
, cbpeer
.update
);
382 QemuClipboardInfo
*info
= data
;
383 QemuClipboardSelection s
= info
->selection
;
384 QemuClipboardType type
;
385 bool self_update
= info
->owner
== &vd
->cbpeer
;
387 if (info
!= vd
->cbinfo
[s
]) {
388 qemu_clipboard_info_unref(vd
->cbinfo
[s
]);
389 vd
->cbinfo
[s
] = qemu_clipboard_info_ref(info
);
390 vd
->cbpending
[s
] = 0;
392 vdagent_send_clipboard_grab(vd
, info
);
401 for (type
= 0; type
< QEMU_CLIPBOARD_TYPE__COUNT
; type
++) {
402 if (vd
->cbpending
[s
] & (1 << type
)) {
403 vd
->cbpending
[s
] &= ~(1 << type
);
404 vdagent_send_clipboard_data(vd
, info
, type
);
409 static void vdagent_clipboard_request(QemuClipboardInfo
*info
,
410 QemuClipboardType qtype
)
412 VDAgentChardev
*vd
= container_of(info
->owner
, VDAgentChardev
, cbpeer
);
413 g_autofree VDAgentMessage
*msg
= g_malloc0(sizeof(VDAgentMessage
) +
414 sizeof(uint32_t) * 2);
415 uint32_t type
= type_qemu_to_vdagent(qtype
);
416 uint8_t *s
= msg
->data
;
417 uint32_t *data
= (uint32_t *)msg
->data
;
419 if (type
== VD_AGENT_CLIPBOARD_NONE
) {
423 if (have_selection(vd
)) {
424 *s
= info
->selection
;
426 msg
->size
+= sizeof(uint32_t);
430 msg
->size
+= sizeof(uint32_t);
432 msg
->type
= VD_AGENT_CLIPBOARD_REQUEST
;
433 vdagent_send_msg(vd
, msg
);
436 static void vdagent_chr_recv_clipboard(VDAgentChardev
*vd
, VDAgentMessage
*msg
)
438 uint8_t s
= VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
;
439 uint32_t size
= msg
->size
;
440 void *data
= msg
->data
;
441 QemuClipboardInfo
*info
;
442 QemuClipboardType type
;
444 if (have_selection(vd
)) {
448 s
= *(uint8_t *)data
;
449 if (s
>= QEMU_CLIPBOARD_SELECTION__COUNT
) {
457 case VD_AGENT_CLIPBOARD_GRAB
:
458 trace_vdagent_cb_grab_selection(GET_NAME(sel_name
, s
));
459 info
= qemu_clipboard_info_new(&vd
->cbpeer
, s
);
460 if (size
> sizeof(uint32_t) * 10) {
462 * spice has 6 types as of 2021. Limiting to 10 entries
463 * so we we have some wiggle room.
467 while (size
>= sizeof(uint32_t)) {
468 trace_vdagent_cb_grab_type(GET_NAME(type_name
, *(uint32_t *)data
));
469 switch (*(uint32_t *)data
) {
470 case VD_AGENT_CLIPBOARD_UTF8_TEXT
:
471 info
->types
[QEMU_CLIPBOARD_TYPE_TEXT
].available
= true;
476 data
+= sizeof(uint32_t);
477 size
-= sizeof(uint32_t);
479 qemu_clipboard_update(info
);
480 qemu_clipboard_info_unref(info
);
482 case VD_AGENT_CLIPBOARD_REQUEST
:
483 if (size
< sizeof(uint32_t)) {
486 switch (*(uint32_t *)data
) {
487 case VD_AGENT_CLIPBOARD_UTF8_TEXT
:
488 type
= QEMU_CLIPBOARD_TYPE_TEXT
;
494 vd
->cbinfo
[s
]->types
[type
].available
&&
495 vd
->cbinfo
[s
]->owner
!= &vd
->cbpeer
) {
496 if (vd
->cbinfo
[s
]->types
[type
].data
) {
497 vdagent_send_clipboard_data(vd
, vd
->cbinfo
[s
], type
);
499 vd
->cbpending
[s
] |= (1 << type
);
500 qemu_clipboard_request(vd
->cbinfo
[s
], type
);
504 case VD_AGENT_CLIPBOARD
: /* data */
505 if (size
< sizeof(uint32_t)) {
508 switch (*(uint32_t *)data
) {
509 case VD_AGENT_CLIPBOARD_UTF8_TEXT
:
510 type
= QEMU_CLIPBOARD_TYPE_TEXT
;
517 qemu_clipboard_set_data(&vd
->cbpeer
, vd
->cbinfo
[s
], type
,
520 case VD_AGENT_CLIPBOARD_RELEASE
: /* data */
522 vd
->cbinfo
[s
]->owner
== &vd
->cbpeer
) {
523 /* set empty clipboard info */
524 info
= qemu_clipboard_info_new(NULL
, s
);
525 qemu_clipboard_update(info
);
526 qemu_clipboard_info_unref(info
);
532 /* ------------------------------------------------------------------ */
533 /* chardev backend */
535 static void vdagent_chr_open(Chardev
*chr
,
536 ChardevBackend
*backend
,
540 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(chr
);
541 ChardevQemuVDAgent
*cfg
= backend
->u
.qemu_vdagent
.data
;
543 #if defined(HOST_WORDS_BIGENDIAN)
545 * TODO: vdagent protocol is defined to be LE,
546 * so we have to byteswap everything on BE hosts.
548 error_setg(errp
, "vdagent is not supported on bigendian hosts");
552 vd
->mouse
= VDAGENT_MOUSE_DEFAULT
;
553 if (cfg
->has_mouse
) {
554 vd
->mouse
= cfg
->mouse
;
557 vd
->clipboard
= VDAGENT_CLIPBOARD_DEFAULT
;
558 if (cfg
->has_clipboard
) {
559 vd
->clipboard
= cfg
->clipboard
;
563 vd
->mouse_hs
= qemu_input_handler_register(&vd
->mouse_dev
,
564 &vdagent_mouse_handler
);
570 static void vdagent_chr_recv_caps(VDAgentChardev
*vd
, VDAgentMessage
*msg
)
572 VDAgentAnnounceCapabilities
*caps
= (void *)msg
->data
;
575 if (msg
->size
< (sizeof(VDAgentAnnounceCapabilities
) +
580 for (i
= 0; i
< ARRAY_SIZE(cap_name
); i
++) {
581 if (caps
->caps
[0] & (1 << i
)) {
582 trace_vdagent_peer_cap(GET_NAME(cap_name
, i
));
586 vd
->caps
= caps
->caps
[0];
588 vdagent_send_caps(vd
);
590 if (have_mouse(vd
) && vd
->mouse_hs
) {
591 qemu_input_handler_activate(vd
->mouse_hs
);
593 if (have_clipboard(vd
) && vd
->cbpeer
.update
.notify
== NULL
) {
594 vd
->cbpeer
.name
= "vdagent";
595 vd
->cbpeer
.update
.notify
= vdagent_clipboard_notify
;
596 vd
->cbpeer
.request
= vdagent_clipboard_request
;
597 qemu_clipboard_peer_register(&vd
->cbpeer
);
601 static void vdagent_chr_recv_msg(VDAgentChardev
*vd
, VDAgentMessage
*msg
)
603 trace_vdagent_recv_msg(GET_NAME(msg_name
, msg
->type
), msg
->size
);
606 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
607 vdagent_chr_recv_caps(vd
, msg
);
609 case VD_AGENT_CLIPBOARD
:
610 case VD_AGENT_CLIPBOARD_GRAB
:
611 case VD_AGENT_CLIPBOARD_REQUEST
:
612 case VD_AGENT_CLIPBOARD_RELEASE
:
613 if (have_clipboard(vd
)) {
614 vdagent_chr_recv_clipboard(vd
, msg
);
622 static void vdagent_reset_xbuf(VDAgentChardev
*vd
)
624 g_clear_pointer(&vd
->xbuf
, g_free
);
629 static void vdagent_chr_recv_chunk(VDAgentChardev
*vd
)
631 VDAgentMessage
*msg
= (void *)vd
->msgbuf
;
634 if (vd
->msgsize
< sizeof(*msg
)) {
635 error_report("%s: message too small: %d < %zd", __func__
,
636 vd
->msgsize
, sizeof(*msg
));
639 if (vd
->msgsize
== msg
->size
+ sizeof(*msg
)) {
640 vdagent_chr_recv_msg(vd
, msg
);
646 vd
->xsize
= msg
->size
+ sizeof(*msg
);
647 vd
->xbuf
= g_malloc0(vd
->xsize
);
650 if (vd
->xoff
+ vd
->msgsize
> vd
->xsize
) {
651 error_report("%s: Oops: %d+%d > %d", __func__
,
652 vd
->xoff
, vd
->msgsize
, vd
->xsize
);
653 vdagent_reset_xbuf(vd
);
657 memcpy(vd
->xbuf
+ vd
->xoff
, vd
->msgbuf
, vd
->msgsize
);
658 vd
->xoff
+= vd
->msgsize
;
659 if (vd
->xoff
< vd
->xsize
) {
663 msg
= (void *)vd
->xbuf
;
664 vdagent_chr_recv_msg(vd
, msg
);
665 vdagent_reset_xbuf(vd
);
668 static void vdagent_reset_bufs(VDAgentChardev
*vd
)
670 memset(&vd
->chunk
, 0, sizeof(vd
->chunk
));
677 static int vdagent_chr_write(Chardev
*chr
, const uint8_t *buf
, int len
)
679 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(chr
);
680 uint32_t copy
, ret
= len
;
683 if (vd
->chunksize
< sizeof(vd
->chunk
)) {
684 copy
= sizeof(vd
->chunk
) - vd
->chunksize
;
688 memcpy((void *)(&vd
->chunk
) + vd
->chunksize
, buf
, copy
);
689 vd
->chunksize
+= copy
;
692 if (vd
->chunksize
< sizeof(vd
->chunk
)) {
696 assert(vd
->msgbuf
== NULL
);
697 vd
->msgbuf
= g_malloc0(vd
->chunk
.size
);
700 copy
= vd
->chunk
.size
- vd
->msgsize
;
704 memcpy(vd
->msgbuf
+ vd
->msgsize
, buf
, copy
);
709 if (vd
->msgsize
== vd
->chunk
.size
) {
710 trace_vdagent_recv_chunk(vd
->chunk
.size
);
711 vdagent_chr_recv_chunk(vd
);
712 vdagent_reset_bufs(vd
);
719 static void vdagent_chr_accept_input(Chardev
*chr
)
721 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(chr
);
723 vdagent_send_buf(vd
);
726 static void vdagent_chr_set_fe_open(struct Chardev
*chr
, int fe_open
)
728 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(chr
);
731 trace_vdagent_close();
733 vdagent_reset_bufs(vd
);
736 qemu_input_handler_deactivate(vd
->mouse_hs
);
738 if (vd
->cbpeer
.update
.notify
) {
739 qemu_clipboard_peer_unregister(&vd
->cbpeer
);
740 memset(&vd
->cbpeer
, 0, sizeof(vd
->cbpeer
));
745 trace_vdagent_open();
748 static void vdagent_chr_parse(QemuOpts
*opts
, ChardevBackend
*backend
,
751 ChardevQemuVDAgent
*cfg
;
753 backend
->type
= CHARDEV_BACKEND_KIND_QEMU_VDAGENT
;
754 cfg
= backend
->u
.qemu_vdagent
.data
= g_new0(ChardevQemuVDAgent
, 1);
755 qemu_chr_parse_common(opts
, qapi_ChardevQemuVDAgent_base(cfg
));
756 cfg
->has_mouse
= true;
757 cfg
->mouse
= qemu_opt_get_bool(opts
, "mouse", VDAGENT_MOUSE_DEFAULT
);
758 cfg
->has_clipboard
= true;
759 cfg
->clipboard
= qemu_opt_get_bool(opts
, "clipboard", VDAGENT_CLIPBOARD_DEFAULT
);
762 /* ------------------------------------------------------------------ */
764 static void vdagent_chr_class_init(ObjectClass
*oc
, void *data
)
766 ChardevClass
*cc
= CHARDEV_CLASS(oc
);
768 cc
->parse
= vdagent_chr_parse
;
769 cc
->open
= vdagent_chr_open
;
770 cc
->chr_write
= vdagent_chr_write
;
771 cc
->chr_set_fe_open
= vdagent_chr_set_fe_open
;
772 cc
->chr_accept_input
= vdagent_chr_accept_input
;
775 static void vdagent_chr_init(Object
*obj
)
777 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(obj
);
779 buffer_init(&vd
->outbuf
, "vdagent-outbuf");
782 static void vdagent_chr_fini(Object
*obj
)
784 VDAgentChardev
*vd
= QEMU_VDAGENT_CHARDEV(obj
);
786 buffer_free(&vd
->outbuf
);
789 static const TypeInfo vdagent_chr_type_info
= {
790 .name
= TYPE_CHARDEV_QEMU_VDAGENT
,
791 .parent
= TYPE_CHARDEV
,
792 .instance_size
= sizeof(VDAgentChardev
),
793 .instance_init
= vdagent_chr_init
,
794 .instance_finalize
= vdagent_chr_fini
,
795 .class_init
= vdagent_chr_class_init
,
798 static void register_types(void)
800 type_register_static(&vdagent_chr_type_info
);
803 type_init(register_types
);