On Fri, Apr 23, 2021 at 12:34 PM Gerd Hoffmann <kra...@redhat.com> wrote:
> This patch adds support for mouse messages to the vdagent > implementation. This can be enabled/disabled using the new > 'mouse' parameter for the vdagent chardev. Default is on. > > Signed-off-by: Gerd Hoffmann <kra...@redhat.com> > Reviewed-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- > chardev/char.c | 3 + > ui/vdagent.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++ > qapi/char.json | 4 +- > 3 files changed, 156 insertions(+), 1 deletion(-) > > diff --git a/chardev/char.c b/chardev/char.c > index 398f09df19cd..9714057541fb 100644 > --- a/chardev/char.c > +++ b/chardev/char.c > @@ -932,6 +932,9 @@ QemuOptsList qemu_chardev_opts = { > },{ > .name = "logappend", > .type = QEMU_OPT_BOOL, > + },{ > + .name = "mouse", > + .type = QEMU_OPT_BOOL, > #ifdef CONFIG_LINUX > },{ > .name = "tight", > diff --git a/ui/vdagent.c b/ui/vdagent.c > index 98d1a2ee3415..e914a33bae20 100644 > --- a/ui/vdagent.c > +++ b/ui/vdagent.c > @@ -1,15 +1,25 @@ > #include "qemu/osdep.h" > #include "include/qemu-common.h" > #include "chardev/char.h" > +#include "hw/qdev-core.h" > +#include "qemu/option.h" > +#include "ui/console.h" > +#include "ui/input.h" > #include "trace.h" > > #include "qapi/qapi-types-char.h" > +#include "qapi/qapi-types-ui.h" > > #include "spice/vd_agent.h" > > +#define VDAGENT_MOUSE_DEFAULT true > + > struct VDAgentChardev { > Chardev parent; > > + /* config */ > + bool mouse; > + > /* guest vdagent */ > uint32_t caps; > VDIChunkHeader chunk; > @@ -18,6 +28,14 @@ struct VDAgentChardev { > uint32_t msgsize; > uint8_t *xbuf; > uint32_t xoff, xsize; > + > + /* mouse */ > + DeviceState mouse_dev; > + uint32_t mouse_x; > + uint32_t mouse_y; > + uint32_t mouse_btn; > + uint32_t mouse_display; > + QemuInputHandlerState *mouse_hs; > }; > typedef struct VDAgentChardev VDAgentChardev; > > @@ -122,13 +140,113 @@ static void vdagent_send_caps(VDAgentChardev *vd) > g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) + > > sizeof(VDAgentAnnounceCapabilities) + > sizeof(uint32_t)); > + VDAgentAnnounceCapabilities *caps = (void *)msg->data; > > msg->type = VD_AGENT_ANNOUNCE_CAPABILITIES; > msg->size = sizeof(VDAgentAnnounceCapabilities) + sizeof(uint32_t); > + if (vd->mouse) { > + caps->caps[0] |= (1 << VD_AGENT_CAP_MOUSE_STATE); > + } > > vdagent_send_msg(vd, msg); > } > > +/* ------------------------------------------------------------------ */ > +/* mouse events */ > + > +static bool have_mouse(VDAgentChardev *vd) > +{ > + return vd->mouse && > + (vd->caps & (1 << VD_AGENT_CAP_MOUSE_STATE)); > +} > + > +static void vdagent_send_mouse(VDAgentChardev *vd) > +{ > + g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) + > + sizeof(VDAgentMouseState)); > + VDAgentMouseState *mouse = (void *)msg->data; > + > + msg->type = VD_AGENT_MOUSE_STATE; > + msg->size = sizeof(VDAgentMouseState); > + > + mouse->x = vd->mouse_x; > + mouse->y = vd->mouse_y; > + mouse->buttons = vd->mouse_btn; > + mouse->display_id = vd->mouse_display; > + > + vdagent_send_msg(vd, msg); > +} > + > +static void vdagent_pointer_event(DeviceState *dev, QemuConsole *src, > + InputEvent *evt) > +{ > + static const int bmap[INPUT_BUTTON__MAX] = { > + [INPUT_BUTTON_LEFT] = VD_AGENT_LBUTTON_MASK, > + [INPUT_BUTTON_RIGHT] = VD_AGENT_RBUTTON_MASK, > + [INPUT_BUTTON_MIDDLE] = VD_AGENT_MBUTTON_MASK, > + [INPUT_BUTTON_WHEEL_UP] = VD_AGENT_UBUTTON_MASK, > + [INPUT_BUTTON_WHEEL_DOWN] = VD_AGENT_DBUTTON_MASK, > +#if 0 > + [INPUT_BUTTON_SIDE] = VD_AGENT_SBUTTON_MASK, > + [INPUT_BUTTON_EXTRA] = VD_AGENT_EBUTTON_MASK, > +#endif > + }; > + > + VDAgentChardev *vd = container_of(dev, struct VDAgentChardev, > mouse_dev); > + InputMoveEvent *move; > + InputBtnEvent *btn; > + uint32_t xres, yres; > + > + switch (evt->type) { > + case INPUT_EVENT_KIND_ABS: > + move = evt->u.abs.data; > + xres = qemu_console_get_width(src, 1024); > + yres = qemu_console_get_height(src, 768); > + if (move->axis == INPUT_AXIS_X) { > + vd->mouse_x = qemu_input_scale_axis(move->value, > + INPUT_EVENT_ABS_MIN, > + INPUT_EVENT_ABS_MAX, > + 0, xres); > + } else if (move->axis == INPUT_AXIS_Y) { > + vd->mouse_y = qemu_input_scale_axis(move->value, > + INPUT_EVENT_ABS_MIN, > + INPUT_EVENT_ABS_MAX, > + 0, yres); > + } > + vd->mouse_display = qemu_console_get_index(src); > + break; > + > + case INPUT_EVENT_KIND_BTN: > + btn = evt->u.btn.data; > + if (btn->down) { > + vd->mouse_btn |= bmap[btn->button]; > + } else { > + vd->mouse_btn &= ~bmap[btn->button]; > + } > + break; > + > + default: > + /* keep gcc happy */ > + break; > + } > +} > + > +static void vdagent_pointer_sync(DeviceState *dev) > +{ > + VDAgentChardev *vd = container_of(dev, struct VDAgentChardev, > mouse_dev); > + > + if (vd->caps & (1 << VD_AGENT_CAP_MOUSE_STATE)) { > + vdagent_send_mouse(vd); > + } > +} > + > +static QemuInputHandler vdagent_mouse_handler = { > + .name = "vdagent mouse", > + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, > + .event = vdagent_pointer_event, > + .sync = vdagent_pointer_sync, > +}; > + > /* ------------------------------------------------------------------ */ > /* chardev backend */ > > @@ -137,6 +255,19 @@ static void vdagent_chr_open(Chardev *chr, > bool *be_opened, > Error **errp) > { > + VDAgentChardev *vd = VDAGENT_CHARDEV(chr); > + ChardevVDAgent *cfg = backend->u.vdagent.data; > + > + vd->mouse = VDAGENT_MOUSE_DEFAULT; > + if (cfg->has_mouse) { > + vd->mouse = cfg->mouse; > + } > + > + if (vd->mouse) { > + vd->mouse_hs = qemu_input_handler_register(&vd->mouse_dev, > + > &vdagent_mouse_handler); > + } > + > *be_opened = true; > } > > @@ -160,6 +291,9 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, > VDAgentMessage *msg) > if (caps->request) { > vdagent_send_caps(vd); > } > + if (have_mouse(vd) && vd->mouse_hs) { > + qemu_input_handler_activate(vd->mouse_hs); > + } > } > > static void vdagent_chr_recv_msg(VDAgentChardev *vd, VDAgentMessage *msg) > @@ -282,18 +416,34 @@ static void vdagent_chr_set_fe_open(struct Chardev > *chr, int fe_open) > /* reset state */ > vdagent_reset_bufs(vd); > vd->caps = 0; > + if (vd->mouse_hs) { > + qemu_input_handler_deactivate(vd->mouse_hs); > + } > return; > } > > trace_vdagent_open(); > } > > +static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend, > + Error **errp) > +{ > + ChardevVDAgent *cfg; > + > + backend->type = CHARDEV_BACKEND_KIND_VDAGENT; > + cfg = backend->u.vdagent.data = g_new0(ChardevVDAgent, 1); > + qemu_chr_parse_common(opts, qapi_ChardevVDAgent_base(cfg)); > + cfg->has_mouse = true; > + cfg->mouse = qemu_opt_get_bool(opts, "mouse", VDAGENT_MOUSE_DEFAULT); > +} > + > /* ------------------------------------------------------------------ */ > > static void vdagent_chr_class_init(ObjectClass *oc, void *data) > { > ChardevClass *cc = CHARDEV_CLASS(oc); > > + cc->parse = vdagent_chr_parse; > cc->open = vdagent_chr_open; > cc->chr_write = vdagent_chr_write; > cc->chr_set_fe_open = vdagent_chr_set_fe_open; > diff --git a/qapi/char.json b/qapi/char.json > index ca5a85f76b3e..880aa8f73333 100644 > --- a/qapi/char.json > +++ b/qapi/char.json > @@ -395,11 +395,13 @@ > # > # Configuration info for vdagent. > # > +# @mouse: enable/disable mouse, default is enabled. > +# > # Since: 6.1 > # > ## > { 'struct': 'ChardevVDAgent', > - 'data': { }, > + 'data': { '*mouse': 'bool' }, > 'base': 'ChardevCommon', > 'if': 'defined(CONFIG_SPICE_PROTOCOL)' } > > -- > 2.30.2 > >