[Spice-devel] paravirtual mouse/tablet, v5

Gerd Hoffmann kraxel at redhat.com
Fri Apr 11 06:34:58 PDT 2014


On Mi, 2011-02-02 at 14:48 +1000, Peter Hutterer wrote:
       ^^^^^^^^^^
FYI: It's been a while ...

> sorry, late again. conference last week.
> 
> On Thu, Jan 27, 2011 at 02:11:35PM +0100, Gerd Hoffmann wrote:
> > Next revision the pvmouse protocol.  It is quite different now, I've
> > decided to move to a model with one message per updated value,
> > simliar to the linux input layer.  There isn't a "mouse move"
> > message any more.  A mouse move event will be three messages now:
> > one to update X, one to update Y and a third sync message to mark
> > the end of the message block.  That should be *alot* easier to
> > extend in the future.
> 
> I like this approach, it's a lot more flexible and quite close to what the
> linux kernel already does.
> Having said that, I'm thinking why not do exactly what the linux kernel
> does?

... but there is working code now, doing exactly that.  There is a new
virtio input device, which basically sends evdev events over virtio.

specs:
  http://www.kraxel.org/cgit/virtio-spec/log/?h=virtio-input

linux kernel:
  http://www.kraxel.org/cgit/linux/log/?h=virtio-input

qemu (also containing some unrelated qemu input stuff):
  http://www.kraxel.org/cgit/qemu/log/?h=rebase/input-wip

Of course it's not limited to a mouse, there is a virtio keyboard too,
and we can pass through any host input device to the guest.

cheers,
  Gerd

[ leaving full quote below for those which might look at it
  for historical interest ]

> * the kernel has a stable API that has a number of users and is well
> understood, plus 
> * it allows for code re-use on the consumer side
> * you'd need to do protocol conversion anyway. having the same API means at
> least one platform (i.e. linux) can skip the conversion.
> * MT is being worked on by a number of people and two protocols that cater
> for current hardware (with and without HW tracking features). trying to
> duplicate this means we'll likely re-do the same mistakes.
> 
> fwiw, this protocol is already quite similar
>     MSG_SYNC is SYN_REPORT
>     MSG_POINT is SYN_MT_REPORT
>     AXIS x is the same as EV_ABS + ABS_X
> 
> there may be some optimization for networks of course if you want to change
> the message format but at least the principle seems sound enough.
> 
> > Header file is attached.  Comments are welcome.
> > 
> > thanks,
> >   Gerd
> 
> > #ifndef __QEMU_PVMOUSE__
> > #define __QEMU_PVMOUSE__ 1
> > 
> > /*
> >  * qemu patavirtual mouse/tablet interface
> >  *
> >  * quick overview
> >  * ==============
> >  *
> >  * device initialization
> >  * ---------------------
> >  *
> >  *  (1) host sends INIT with supported feature bits
> >  *  (2) guests sends ACK to ack the features it is
> >  *      able to handle (and willing to use).
> 
> what's the effect of a guest not able to handle certain features? The host
> filters them?
> 
> >  *  (3) host sends a INFO message for each button and
> >  *      each axis supported.  More INFO messages might
> >  *      follow for features added to the protocol later
> >  *      on if enabled by the INIT+ACK handshake.
> >  *      A SYNC message marks the end of the device
> >  *      information messages.
> >  *
> >  * input event reporting
> >  * ---------------------
> >  *
> >  *  host sends one or more BTN_DOWN, BTN_UP and AXIS messages,
> >  *  followed by a SYNC.  A button press would be just BTN_DOWN+SYNC.
> >  *  A mouse move would be two AXIS messages (one for x, one for y)
> >  *  followed by SYNC.
> 
> fwiw, kernel doesn't have BTN_DOWN events but rather BTN_FOO 0 or 1 events
> (for released and pressed, respectively). Same for keys, which also allows
> keys to have a value of 2 for autorepeat.
> 
> it provides more symmetry as well because currently you have
>     AXIS <axis code> <axis value>
>     BTN_DOWN <button code>
> 
> so axis and button_down are asymmetrical in format.
> alternatively, you could use
>     AXIS <axis code> <axis value>
>     BTN <button code> <button value>
> 
> thus making the message format identical.
> 
> >  *
> >  * multitouch events
> >  * -----------------
> >  *
> >  *  Each reported touch point starts with a POINT message, followed by
> >  *  multiple AXIS messages (at least x+y, most likely also pressure).
> >  *  The whole group is followed by SYNC.  A report for two fingers
> >  *  would look like this:
> >  *
> >  *   POINT
> >  *     AXIS x=23
> >  *     AXIS y=42
> >  *     AXIS pressure=3
> >  *   POINT
> >  *     AXIS x=78
> >  *     AXIS y=56
> >  *     AXIS pressure=4
> >  *   SYNC
> >  *
> >  *  In case the device supports ID tracking the POINT message will
> >  *  carry the ID.  Updates need only be sent for points which did
> >  *  change.  IDs are added by using them the first time.  IDs are
> >  *  invalidated when the finger is lifted (aka pressure=0).
> 
> how about devices that support hovering, where the pressure is 0 but the
> touchpoint is still clearly in proximity and active?
> 
> >  *  In case the device doesn't support ID tracking each report must
> >  *  include all current touch points (in unspecified order).
> >  *
> >  */
> > 
> > #include <inttypes.h>
> > 
> > /*
> >  * our virtio-serial channel name(s)
> >  */
> > #define QEMU_PVMOUSE_FORMAT "org.qemu.pvmouse.%d"
> > 
> > enum qemu_pvmouse_msg_type {
> >     /* feature flag negotiation */
> >     QEMU_PVMOUSE_MSG_INIT,
> >     QEMU_PVMOUSE_MSG_ACK,
> >     /* device information */
> >     QEMU_PVMOUSE_MSG_AXIS_INFO,
> >     QEMU_PVMOUSE_MSG_BUTTON_INFO,
> >     /* device events */
> >     QEMU_PVMOUSE_MSG_BTN_DOWN,
> >     QEMU_PVMOUSE_MSG_BTN_UP,
> >     QEMU_PVMOUSE_MSG_AXIS,
> >     /* message grouping */
> >     QEMU_PVMOUSE_MSG_POINT,
> >     QEMU_PVMOUSE_MSG_SYNC,
> > };
> > 
> > typedef enum qemu_pvmouse_features {
> >     QEMU_PVMOUSE_FEATURE_MULTITOUCH,
> > };
> 
> with or without tracking? I can't see any flag that tells us that ahead of
> time but for consumers it's quite important.
> 
> how many simultaneous touchpoint does the device support?
> 
> again, quick overview of the kernel here:
> MT protocol A is essentially just positions + MT_SYN (POINT) events.
> MT protocol B for tracked uses TRACKING_ID as well as MT_SLOT. the slots say
> how many simultaneous trackpoints can go on. tracking ID is >= 0 for any
> given touchpoint and -1 to cancel a touchpoint in the current slot.
> 
> So without kernel-side filtering, you'd get
> 
> ABS_MT_SLOT             0
> ABS_MT_TRACKING_ID      23
> ABS_MT_POSITION_X       100 
> ABS_MT_SLOT             1
> ABS_MT_TRACKING_ID      28
> ABS_MT_POSITION_X       23
> ABS_MT_POSITION_Y       156
> SYN_REPORT
> -----------------------------
> ABS_MT_SLOT             0
> ABS_MT_TRACKING_ID      -1
> ABS_MT_SLOT             1
> ABS_MT_TRACKING_ID      28
> ABS_MT_POSITION_X       30
> ABS_MT_POSITION_Y       162
> SYN_REPORT
> -----------------------------
> ABS_MT_SLOT             0
> ABS_MT_TRACKING_ID      29
> ABS_MT_POSITION_X       600
> ABS_MT_POSITION_Y       876
> ABS_MT_SLOT             1
> ABS_MT_TRACKING_ID      28
> ABS_MT_POSITION_X       30
> ABS_MT_POSITION_Y       162
> SYN_REPORT
> 
> so you have three events, first two touchpoints, then first touchpoint is
> lifted, new touchpoint is created.
> 
> > /*
> >  * QEMU_PVMOUSE_MSG_INIT, host -> guest
> >  * First message.  Sent before any other event.
> >  */
> > typedef struct qemu_pvmouse_init {
> >     uint32_t features;        /* qemu_pvmouse_features */
> > } qemu_pvmouse_init;
> > 
> > /*
> >  * QEMU_PVMOUSE_MSG_ACK, guest -> host
> >  * Sent after pvmouse_init.  Host will not send
> >  * additional messages until this is received.
> >  */
> > typedef struct qemu_pvmouse_ack {
> >     uint32_t features; /* qemu_pvtable_features */
> > };
> > 
> > 
> > enum qemu_pvmouse_axis_type {
> >     /* absolute */
> >     QEMU_PVMOUSE_AXIS_POS_X = 1,
> >     QEMU_PVMOUSE_AXIS_POS_Y,
> >     QEMU_PVMOUSE_AXIS_PRESSURE,
> > 
> >     /* relative */
> >     QEMU_PVMOUSE_AXIS_REL_X = 257,
> >     QEMU_PVMOUSE_AXIS_REL_Y,
> >     QEMU_PVMOUSE_AXIS_WHEEL_VERT,
> >     QEMU_PVMOUSE_AXIS_WHEEL_HORIZ,
> > };
> > 
> > enum qemu_pvmouse_axis_flags {
> >     QEMU_PVMOUSE_AXIS_PEN,
> >     QEMU_PVMOUSE_AXIS_TOUCH,
> >     QEMU_PVMOUSE_AXIS_MULTITOUCH,
> >     QEMU_PVMOUSE_AXIS_POINT_ID,
> > };
> > #define QEMU_PVMOUSE_AXIS_MASK_PEN         (1 << QEMU_PVMOUSE_AXIS_PEN)
> > #define QEMU_PVMOUSE_AXIS_MASK_TOUCH       (1 << QEMU_PVMOUSE_AXIS_TOUCH)
> > #define QEMU_PVMOUSE_AXIS_MASK_MULTITOUCH  (1 << QEMU_PVMOUSE_AXIS_MULTITOUCH)
> > #define QEMU_PVMOUSE_AXIS_MASK_POINT_ID    (1 << QEMU_PVMOUSE_AXIS_ID)
> 
> fwiw, the kernel already defines 14 tools, not that far off until you run
> out of tools on 32 bits. why not make the initial feature negotiation a
> stream of events instead of a mask?
> 
> Cheers,
>   Peter
> > 
> > /*
> >  * QEMU_PVMOUSE_MSG_AXIS_INFO, host -> guest
> >  * Send axis informations.
> >  */
> > typedef struct qemu_pvmouse_axis_info {
> >     uint32_t axis_id;
> >     uint32_t type;
> >     uint32_t flags;
> >     int32_t  min;
> >     int32_t  max;
> >     char     label[];
> > };
> > 
> > /*
> >  * QEMU_PVMOUSE_MSG_AXIS, host -> guest
> >  * Send mouse/pen/finger/wheel move events.
> >  */
> > typedef struct qemu_pvmouse_axis_event {
> >     uint32_t axis_id;
> >     int32_t  value;
> > };
> > 
> > /*
> >  * QEMU_PVMOUSE_MSG_BUTTON_INFO, host -> guest
> >  * Send button informations.
> >  */
> > typedef struct qemu_pvmouse_button_info {
> >     uint32_t button_id;
> >     char     label[];
> > };
> > 
> > /*
> >  * QEMU_PVMOUSE_MSG_BTN_{DOWN,UP}, host -> guest
> >  * Send button press+release events.
> >  */
> > typedef struct qemu_pvmouse_btn_event {
> >     uint32_t button_id;
> > };
> > 
> > /*
> >  * QEMU_PVMOUSE_MSG_POINT, host -> guest
> >  * marks the start of a point group for multitouch devices.
> >  */
> > typedef struct qemu_pvmouse_point {
> >     uint32_t id;
> > };
> > 
> > /*
> >  * QEMU_PVMOUSE_MSG_SYNC, host -> guest
> >  * Marks the end of a message group which belongs together
> >  * and carries the time stamp for all those events.
> >  *
> >  * The timestamp is specified in nanoseconds.  Timebase is undefined.
> >  * This is supposed to be used to figure how much time passed between
> >  * two events, to decide whenever two mouse clicks should be
> >  * interpreted as double click or not and simliar stuff.
> >  */
> > typedef struct qemu_pvmouse_sync {
> >     uint64_t timestamp;
> > };
> > 
> > 
> > typedef struct qemu_pvmouse_header {
> >     uint32_t size;            /* whole message size */
> >     uint32_t type;            /* qemu_pvmouse_type */
> > } qemu_pvmouse_header;
> > 
> > typedef union qemu_pvmouse_payload {
> >     qemu_pvmouse_init     init;
> >     qemu_pvmouse_ack      ack;
> >     /* ... */
> > };
> > 
> > typedef struct qemu_pvmouse_message {
> >     qemu_pvmouse_header  hdr;
> >     qemu_pvmouse_payload data;
> > } qemu_pvmouse_message;
> > 
> > #endif /* __QEMU_PVMOUSE__ */
> 




More information about the Spice-devel mailing list