rust-windowing / winit

Window handling library in pure Rust
https://docs.rs/winit/
Apache License 2.0
4.75k stars 893 forks source link

MouseMotion event returns absolute instead of relative values, when running Linux in a VM #946

Open iceiix opened 5 years ago

iceiix commented 5 years ago

I'm listening for glutin::DeviceEvent::MouseMotion{delta:(xrel, yrel)} and it works fine on most systems, but Ubuntu 18.04.1 in VMware Fusion causes xrel and yrel to be set to large absolute values as the mouse moves, for example:

MouseMotion 39128.40293884277 25325.613555908203 MouseMotion 39128.40293884277 25371.612854003906 MouseMotion 39128.40293884277 25416.6121673584

instead of the expected relative delta values. When implementing mouse look in a game using this event, this causes the player to rapidly spin instead of look where their mouse is pointing. SDL2 has similar issues: https://bugzilla.libsdl.org/show_bug.cgi?id=2150 https://bugzilla.libsdl.org/show_bug.cgi?id=2954 https://stackoverflow.com/questions/25576438/sdl-getrelativemousestate-strange-behaviour, but there is an option to enable a "relative mode warp" hint in SDL2, does/could Glutin have something similar?

francesca64 commented 5 years ago

Thanks for reporting this! For future reference, this issue relates to code in winit, which glutin depends on.

does/could Glutin have something similar?

We currently don't, but if the aforementioned fix in SDL works fine, then it shouldn't be hard to make a similar fix here. Relevant code: https://github.com/spurious/SDL-mirror/blob/d3d9a9ddd00f710835a3c133d6c196b7c4bd1792/src/events/SDL_mouse.c#L302

It would be awesome if you'd like to try making a PR for this to winit, since it could take a bit of time before I'd be able to get to it (and I wouldn't be able to repro/test this myself).

goddessfreya commented 5 years ago

Closing in favor of upstream. https://github.com/rust-windowing/winit/issues/876

psychon commented 5 years ago

What is necessary to reproduce this? Running cargo run --example window on latest master with X11 and moving the cursor around results in lots of these, which look quite relative to me:

DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Motion { axis: 0, value: -6.0 } }
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Motion { axis: 1, value: 0.0 } }
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: MouseMotion { delta: (-6.0, 0.0) } }
WindowEvent { window_id: WindowId(X(WindowId(31457281))), event: CursorMoved { device_id: DeviceId(X(DeviceId(2))), position: LogicalPosition { x: 218.02351262019232, y: 221.65562086838943 }, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } }
WindowEvent { window_id: WindowId(X(WindowId(31457281))), event: AxisMotion { device_id: DeviceId(X(DeviceId(2))), axis: 0, value: 661.1921524943318 } }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2554, tv_nsec: 982201931 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Motion { axis: 0, value: -12.0 } }
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Motion { axis: 1, value: 1.0 } }
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: MouseMotion { delta: (-12.0, 1.0) } }
WindowEvent { window_id: WindowId(X(WindowId(31457281))), event: CursorMoved { device_id: DeviceId(X(DeviceId(2))), position: LogicalPosition { x: 198.4947228064904, y: 223.28303410456732 }, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } }
WindowEvent { window_id: WindowId(X(WindowId(31457281))), event: AxisMotion { device_id: DeviceId(X(DeviceId(2))), axis: 0, value: 640.0359673062339 } }
WindowEvent { window_id: WindowId(X(WindowId(31457281))), event: AxisMotion { device_id: DeviceId(X(DeviceId(2))), axis: 1, value: 332.8899452071637 } }
EventsCleared
NewEvents(WaitCancelled { start: Instant { tv_sec: 2554, tv_nsec: 990172372 }, requested_resume: None })
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Motion { axis: 0, value: -8.0 } }
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: Motion { axis: 1, value: 1.0 } }
DeviceEvent { device_id: DeviceId(X(DeviceId(10))), event: MouseMotion { delta: (-8.0, 1.0) } }
WindowEvent { window_id: WindowId(X(WindowId(31457281))), event: CursorMoved { device_id: DeviceId(X(DeviceId(2))), position: LogicalPosition { x: 184.40799654447116, y: 225.04386080228366 }, modifiers: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } }
WindowEvent { window_id: WindowId(X(WindowId(31457281))), event: AxisMotion { device_id: DeviceId(X(DeviceId(2))), axis: 0, value: 624.775324880844 } }
WindowEvent { window_id: WindowId(X(WindowId(31457281))), event: AxisMotion { device_id: DeviceId(X(DeviceId(2))), axis: 1, value: 334.79752551042475 } }
EventsCleared

(Well, at least the values called delta look like deltas)

Osspial commented 5 years ago

@psychon You need to run this in a VM, I believe. I'm not entirely sure what to make of it if you already are and it's working 🤷‍♀

chelmich commented 4 years ago

For what it's worth I was able to recreate this issue as of b6e8dd0d8a51fa7f3324f84d3529b2e2573a529e by using cargo run --example window with the following setup:

I would be glad to try tackling this issue but I'm not really sure where to start.

Edit: After a bit more investigating the culprit seems to be VirtualBox's "mouse integration" device. Allowing the VM to capture the cursor yields correct behavior. Below is the output of xinput list --long on the mouse integration device.

VirtualBox mouse integration                id=9    [slave  pointer  (2)]
    Reporting 7 classes:
        Class originated from: 9. Type: XIButtonClass
        Buttons supported: 7
        Button labels: "Button Left" "Button Middle" "Button Right" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right"
        Button state:
        Class originated from: 9. Type: XIValuatorClass
        Detail for Valuator 0:
          Label: Abs X
          Range: 0.000000 - 65535.000000
          Resolution: 0 units/m
          Mode: absolute
          Current value: 22454.657364
        Class originated from: 9. Type: XIValuatorClass
        Detail for Valuator 1:
          Label: Abs Y
          Range: 0.000000 - 65535.000000
          Resolution: 0 units/m
          Mode: absolute
          Current value: 17652.730637

Compare that to the device used when pointer capture is enabled:

ImExPS/2 Generic Explorer Mouse             id=12   [slave  pointer  (2)]
    Reporting 7 classes:
        Class originated from: 12. Type: XIButtonClass
        Buttons supported: 9
        Button labels: "Button Left" "Button Middle" "Button Right" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right" "Button Side" "Button Extra"
        Button state:
        Class originated from: 12. Type: XIValuatorClass
        Detail for Valuator 0:
          Label: Rel X
          Range: -1.000000 - -1.000000
          Resolution: 0 units/m
          Mode: relative
        Class originated from: 12. Type: XIValuatorClass
        Detail for Valuator 1:
          Label: Rel Y
          Range: -1.000000 - -1.000000
          Resolution: 0 units/m
          Mode: relative

So it looks like some devices only produce absolute coordinates. Any ideas on how best to handle this?

goddessfreya commented 4 years ago

@chelmich Well, there are a couple possible solutions:

I'm leaning towards the second solution, since according to my limited reading of the related SDL issues, this issue, or similar ones, might also affect android and macos, somehow.

cc @Osspial

OvermindDL1 commented 4 years ago

I'm for the enum, knowing the difference between the event type is highly useful in many cases and makes it obvious that it should be handled instead of just ignored.

chelmich commented 4 years ago

I can take a crack at implementing it then. If there's going to be breaking changes they might as well be in 0.20. One thing to note is that the absolute coordinates can be weird because I think they might be based off the monitor on the host machine? I'm not entirely sure.

OvermindDL1 commented 4 years ago

I'm actually thinking, I wonder if it's better to have both delta and absolute position. The delta is always there, the absolute is an Option that may or may not be there (maybe it could be there for the absolute screen position with a non-exclusive mouse in addition to touchscreens and so forth?).

Osspial commented 4 years ago

@goddessfreya That second solution is actually implemented on the dpi-overhaul branch. You run into this same issue when you're processing input from pen/tablet devices.

Caellian commented 5 months ago

I believe this bug stems all the way down from Xlib/libinput (as @chelmich has shown). I'm experiencing it on Arch, lemurs, Openbox, without a VM:

I'm not using winit, just raw Xlib, from a completely unrelated C++ codebase. But I'd like to add to discussion because I've been staring at this for a few days now.

For some reason, XInput returns absolute values for valuators with mode: relative and "Rel" in their name (both scroll and move). This is the case for both my trackpad and mouse. SDL probably just wraps the event data provided by Xi.

Might have to do with libinput and/or the fact that the device is being passed through a virtualization layer.

There's no correct way of amortizing for this because cursors can teleport, synthetic events exist, etc. and so all measurements/comparisons/tracking can give false positives. I'll personally just end up allowing an override through xorg.conf options, but that's not something winit can/should probably do.

EDIT: This weirdly only happened because window type wasn't normal but instead desktop/panel/dock or root (it's conky, that's why it's configurable). On normal windows it works as expected.