[Qemu-devel] [PATCH] ui: input-linux: Add absolute event support

Philippe Voinov posted 1 patch 6 years, 11 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20170505075918.4033-1-philippevoinov@gmail.com
There is a newer version of this series
ui/input-linux.c | 41 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
[Qemu-devel] [PATCH] ui: input-linux: Add absolute event support
Posted by Philippe Voinov 6 years, 11 months ago
This patch adds support for absolute pointer events to the input-linux
subsystem. This support was omitted from the original input-linux patch,
however almost all the code required for it is already in place.

Support for absolute events is especially useful for guests with vga
passthrough. Since they have a physical monitor, none of normal channels
for sending video output (vnc, etc) are used, meaning they also can't be
used to send absolute input events. This leaves QMP as the only option
to send absolute input into vga passthrough guests, which is not its
intended use and is not efficient.

This patch allows, for example, uinput to be used to create virtual
absolute input devices. This lets you build external systems which share
physical input devices between guests. Without absolute input
capability, such external systems can't seamlessly share pointer devices
between guests.

Signed-off-by: Philippe Voinov <philippevoinov@gmail.com>
---
 ui/input-linux.c | 41 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/ui/input-linux.c b/ui/input-linux.c
index dc0613c..7946b87 100644
--- a/ui/input-linux.c
+++ b/ui/input-linux.c
@@ -169,6 +169,10 @@ struct InputLinux {
     bool        has_abs_x;
     int         num_keys;
     int         num_btns;
+    int         abs_x_min;
+    int         abs_x_max;
+    int         abs_y_min;
+    int         abs_y_max;
     struct input_event event;
     int         read_offset;
 
@@ -268,6 +272,11 @@ static void input_linux_event_mouse_button(int button)
     qemu_input_event_sync();
 }
 
+static void input_linux_event_abs(InputAxis axis, int value, int min, int max)
+{
+    qemu_input_queue_abs(NULL, axis, value - min, max - min);
+}
+
 static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
 {
     if (!il->grab_active) {
@@ -314,6 +323,18 @@ static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
             break;
         }
         break;
+    case EV_ABS:
+        switch (event->code) {
+        case ABS_X:
+            input_linux_event_abs(INPUT_AXIS_X, event->value,
+                                  il->abs_x_min, il->abs_x_max);
+            break;
+        case ABS_Y:
+            input_linux_event_abs(INPUT_AXIS_Y, event->value,
+                                  il->abs_y_min, il->abs_y_max);
+            break;
+        }
+        break;
     case EV_SYN:
         qemu_input_event_sync();
         if (il->wheel != 0) {
@@ -351,12 +372,18 @@ static void input_linux_event(void *opaque)
         if (il->num_keys) {
             input_linux_handle_keyboard(il, &il->event);
         }
-        if (il->has_rel_x && il->num_btns) {
+        if ((il->has_rel_x || il->has_abs_x) && il->num_btns) {
             input_linux_handle_mouse(il, &il->event);
         }
     }
 }
 
+static bool input_linux_abs_range_is_valid(int min, int max)
+{
+  int64_t difference = ((int64_t) max) - ((int64_t) min);
+  return difference > 0 && difference <= INT_MAX;
+}
+
 static void input_linux_complete(UserCreatable *uc, Error **errp)
 {
     InputLinux *il = INPUT_LINUX(uc);
@@ -364,6 +391,7 @@ static void input_linux_complete(UserCreatable *uc, Error **errp)
     uint8_t keymap[KEY_CNT / 8], keystate[KEY_CNT / 8];
     unsigned int i;
     int rc, ver;
+    struct input_absinfo absinfo;
 
     if (!il->evdev) {
         error_setg(errp, "no input device specified");
@@ -402,6 +430,17 @@ static void input_linux_complete(UserCreatable *uc, Error **errp)
         rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
         if (absmap & (1 << ABS_X)) {
             il->has_abs_x = true;
+            rc = ioctl(il->fd, EVIOCGABS(ABS_X), &absinfo);
+            il->abs_x_min = absinfo.minimum;
+            il->abs_x_max = absinfo.maximum;
+            rc = ioctl(il->fd, EVIOCGABS(ABS_Y), &absinfo);
+            il->abs_y_min = absinfo.minimum;
+            il->abs_y_max = absinfo.maximum;
+            if (!input_linux_abs_range_is_valid(il->abs_x_min, il->abs_x_max) ||
+                !input_linux_abs_range_is_valid(il->abs_y_min, il->abs_y_max)) {
+                error_setg(errp, "incompatible evdev absolute axis range");
+                goto err_close;
+            }
         }
     }
 
-- 
2.12.2


Re: [Qemu-devel] [PATCH] ui: input-linux: Add absolute event support
Posted by Gerd Hoffmann 6 years, 11 months ago
  Hi,

> This patch allows, for example, uinput to be used to create virtual
> absolute input devices. This lets you build external systems which share
> physical input devices between guests.

Ah, interesting.

> +static bool input_linux_abs_range_is_valid(int min, int max)
> +{
> +  int64_t difference = ((int64_t) max) - ((int64_t) min);
> +  return difference > 0 && difference <= INT_MAX;
> +}

Does this happen in practice?
If so we might consider improving ui/input.c instead.

cheers,
  Gerd


Re: [Qemu-devel] [PATCH] ui: input-linux: Add absolute event support
Posted by Philippe Voinov 6 years, 11 months ago
Hi

I doubt that any physical input device uses the full 32 bit range. The idea
behind this check is more that if someone implements a uinput device and
decides to use the maximum range, passing their values straight through to
ui/input.c would cause weird behavior of the cursor. Exiting with a warning
here is supposed to make this easier to debug - in these cases, the person
implementing the input device can simply use a smaller range.

Aside from that, trying to change ui/input.c is probably a bad idea. The
code
there expects absolute input values to start at 0. To use the full 32 bits
of
an int would require absolute axes to have a different starting position.
This
would require large changes to ui/input.c and possibly also other code using
it. Also there's not much to gain, since we would probably still convert
values
into a range much smaller than 32 bits, as we do now.

Regards
-
Philippe

On Fri, May 5, 2017 at 10:39 AM Gerd Hoffmann <kraxel@redhat.com> wrote:

>   Hi,
>
> > This patch allows, for example, uinput to be used to create virtual
> > absolute input devices. This lets you build external systems which share
> > physical input devices between guests.
>
> Ah, interesting.
>
> > +static bool input_linux_abs_range_is_valid(int min, int max)
> > +{
> > +  int64_t difference = ((int64_t) max) - ((int64_t) min);
> > +  return difference > 0 && difference <= INT_MAX;
> > +}
>
> Does this happen in practice?
> If so we might consider improving ui/input.c instead.
>
> cheers,
>   Gerd
>
>
Re: [Qemu-devel] [PATCH] ui: input-linux: Add absolute event support
Posted by Gerd Hoffmann 6 years, 11 months ago
  Hi,

> Aside from that, trying to change ui/input.c is probably a bad idea.
> The code
> there expects absolute input values to start at 0. To use the full 32
> bits of
> an int would require absolute axes to have a different starting
> position. This
> would require large changes to ui/input.c and possibly also other code
> using
> it.

No.  Internally qemu uses 0 ... INPUT_EVENT_ABS_SIZE.

So only qemu_input_queue_abs() and qemu_input_scale_axis() would need to
be changed, all other code wouldn't notice.

While being at it it probably makes sense to add support for min != 0,
i.e. replace size_in with min_in and max_in in qemu_input_scale_axis, so
you can just pass min + max directly and have the generic input.c code
do the calculations for you.

cheers,
  Gerd