diff mbox series

[v4,5/9] ui/vdagent: add mouse support

Message ID 20210423083351.2096734-6-kraxel@redhat.com
State New
Headers show
Series ui: add vdagent implementation and clipboard support. | expand

Commit Message

Gerd Hoffmann April 23, 2021, 8:33 a.m. UTC
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 <kraxel@redhat.com>
---
 chardev/char.c |   3 +
 ui/vdagent.c   | 150 +++++++++++++++++++++++++++++++++++++++++++++++++
 qapi/char.json |   4 +-
 3 files changed, 156 insertions(+), 1 deletion(-)

Comments

Marc-André Lureau April 27, 2021, 8:56 a.m. UTC | #1
On Fri, Apr 23, 2021 at 12:34 PM Gerd Hoffmann <kraxel@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 <kraxel@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@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
>
>
diff mbox series

Patch

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)' }