Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment with an improved event design #1374

Closed
wants to merge 29 commits into from

Conversation

Osspial
Copy link
Contributor

@Osspial Osspial commented Jan 8, 2020

  • Tested on all platforms changed
  • Compilation warnings were addressed
  • cargo fmt has been run on this branch
  • cargo doc builds successfully
  • Added an entry to CHANGELOG.md if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality
  • Compiles anywhere
  • Compiles everywhere

This PR is a draft for an updated event design that tries to fix the old event API design's numerous flaws. These include:

  • There are too many structural enum variants. Structural variants are annoyingly verbose to match on, and have been universally replaced with tuple variants.

  • Many events aren't flat. Several events contain other structures or enums that need to be imported separately to get matched on, which is an absolute PITA. Examples:

    • If you're matching WindowEvent::MouseInput and want to check if the left mouse button was pressed, you need to import two other types (ElementState and MouseButton). Those shouldn't be necessary.
    • It's impossible to match on keyboard input or touch input without importing separate KeyboardInput or Touch types, which could be removed from the API completely.

    Extraneous types that need to get imported separately have been reduced to an absolute minimum.

  • Events expose useless data. Most WindowEvents have a DeviceId field that just gets set to a meaningless values on all backends except X11, and an arbitrary set of events include ModifiersState despite ModifiersChanged existing. Those have been removed.

  • There's a lot of inconsistency and redundancy. "Mouse" and "Cursor" are used for different purposes, and it's difficult to remember which one is used where. Touch events and mouse events have completely distinct APIs, despite being used for the same thing in a lot of cases. "Touchpad pressure" and "touch pressure" are two entirely distinct events with no overlap. All of these events have been pulled into a consistent set of Pointer events.

  • DeviceEvent doesn't have a design - it's a blob of variants thrown together into one thing. A "Device" isn't a good unit of abstraction, since there are several different device types with almost entirely orthagonal usecases. DeviceEvent has been split into RawPointerEvent and RawKeyboardEvent, and variants that were rarely emitted have been removed. DeviceEvent::Motion has been removed, since the current design is pretty much unusable, it was only emitted on X11, and gamepad support is on the radar.

  • Event::(Suspended|Resumed) don't really belong in the root Event enum, and have been moved into an AppEvent variant. AppEvent could in theory be used in the future for additional application lifecycle events.

  • WindowEvent's lifetime. Not being able to clone or buffer events is annoying, and the events that do need that lifetime should be quarantined off from the ones that don't. Lifetimed window events have been split into WindowEventImmediate.

I think that's a fairly comprehensive list. Please review the API and provide feedback if you have any issues.

Also, PressFlags are used instead of booleans in the button press events, since booleans are absolutely awful to match on when they're in tuple variants, as true and false doesn't have any inherent semantic meaning when casually reading the source code.

@Osspial Osspial changed the title Experiment with an improve event design Experiment with an improved event design Jan 8, 2020
@elinorbgr
Copy link
Contributor

I have not precisely reviewed the actual detailed code, but overall these principles get a 👍 from me.


There is just one thing I'd like to mitigate (but it's clearly on the nitpick level):

There are too many structural enum variants. Structural variants are annoyingly verbose to match on, and have been universally replaced with tuple variants.

In terms of matching, I don't think structural patterns are more verbose thant tuple ones, as long as you don't need to rename the fields:

match event {
    // structural variant
    MouseWhell { device_id, delta, phase, modifiers } => {/* ... */},
    /// tuple variant
    MouseWhell(device_id, delta, phase, modifiers) => {/* ... */}
}

And structural patterns come with the advantage of being imo more ergonomic when you only need to process a subset of the fields, and you get the additional bonus of not needing to match the exact order of the fields:

match event {
    // structural variant
    MouseWhell { delta, ... } => {/* ... */},
    /// tuple variant
    MouseWhell(_, delta, _, _) => {/* ... */}
}

@valkum valkum mentioned this pull request Jan 8, 2020
15 tasks
@Osspial
Copy link
Contributor Author

Osspial commented Jan 8, 2020

@vberger I don't think those complaints are valid when the new API is taken as a whole, since events have been flattened out significantly. Compare the old MouseWheel event:

MouseWheel {
    device_id: DeviceId,
    delta: MouseScrollDelta,
    phase: TouchPhase,
    modifiers: ModifiersState,
},

pub enum MouseScrollDelta {
    LineDelta(f32, f32),
    PixelDelta(LogicalPosition<f64>),
}

to the new scroll events:

ScrollStarted,
ScrollDiscrete(Vector<i32>),
ScrollSmooth(Vector<f64>),
ScrollEnded,

pub struct Vector<T> {
    pub x: T,
    pub y: T,
}

You don't have to worry about how ergonomic it is to ignore data when the events don't contain data that needs to be ignored. The event flattening also helps out with field ordering; no event has more than three fields, and only three events have more than two fields:

WindowEvent::KeyPress(LogicalKey, ScanCode, PressFlags)
WindowEvent::PointerPress(PointerId, PointerButton, PressFlags)
RawKeyboardEvent::Press(Option<LogicalKey>, ScanCode, RawPressFlags)

Personally speaking, I find it much easier to remember "the first field has the keycode, the second field has the scancode, and the third field has boolean flags" than it is to remember the exact names for each of the fields. I constantly get tripped up when matching on the events from memory, since the exact names for things just don't stick, and additionally it's hard to remember which particular events are tuple variants and which are structural variants.

In terms of matching, I don't think structural patterns are more verbose thant tuple ones, as long as you don't need to rename the fields:

This is true if you're just accessing each field, but is not the case if you're matching on each particular field. Compare matching on an A key press in the old API vs the new one:

// old
WindowEvent::KeyboardInput(KeyboardInput{virtual_keycode: Some(VirtualKeyCode::A), state: ElementState::Pressed, ..}) => ()

// new
WindowEvent::KeyPress(Some(LogicalKey::A), _, flags) if flags.is_down() => (),

That's admittedly a somewhat unfair comparison, since the new API does a lot of additional stuff to reduce verbosity, but it should still be clear that even with noise reduced a minimum tuple variants are significantly more readable than structural variants.

@goddessfreya goddessfreya added the S - api Design and usability label Jan 10, 2020
@Ralith
Copy link
Contributor

Ralith commented Jan 11, 2020

WindowEvent::KeyPress(Some(LogicalKey::A), _, flags) if flags.is_down() => ()

Some alternative conceptions, since if we're going to break things we should not consider the status quo to be the only alternative to the proposal:

WindowEvent::KeyPress { key: Some(LogicalKey::A), is_down: true, .. } => ()
WindowEvent::KeyPress(e) if e.key() == Some(LogicalKey::A) && e.is_down() => ()

Both of these similarly require no extra imports, have better forwards-compatibility with future additions, and don't require the reader to guess at the meanings of non-enum (e.g. button number) fields. The former is shorter than the proposal.

@ryanisaacg
Copy link
Contributor

@Ralith's proposed design:

WindowEvent::KeyPress(e) if e.key() == Some(LogicalKey::A) && e.is_down() => ()

appeals to me, especially for the reason of forwards-compat.

@OvermindDL1
Copy link

I especially like the WindowEvent::KeyPress { key: LogicalKey::A, is_down: true, .. } => () one, it's nice to put all the matching code in the head. Though to be honest the great majority of the time will be passing them through an action mapper instead of handling them directly.

@Ralith
Copy link
Contributor

Ralith commented Jan 13, 2020

In lieu of #[non_exhaustive] (which we may want to avoid for MSRV reasons per discussion in #1355), struct variants do not have genuine forwards-compatibility, because exhaustive matches can be written, but non-exhaustive matches will still be more common, and upgrade friction therefore lower, than would be possible with tuple variants. Conversely, newtype variants offer strong forwards-compatibility in the sense that a semver break is not required for new fields to be added.

@Osspial
Copy link
Contributor Author

Osspial commented May 15, 2020

@Ralith's proposed design:

WindowEvent::KeyPress(e) if e.key() == Some(LogicalKey::A) && e.is_down() => ()

appeals to me, especially for the reason of forwards-compat.

I also like that idea, and I've drafted it out for the key-press and pointer-press events. How does this new implementation look to you all?

src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated
Comment on lines 280 to 304
pub(crate) logical_key: Option<LogicalKey>,
pub(crate) scan_code: u32,
pub(crate) is_down: bool,
pub(crate) repeat_count: u32,
pub(crate) is_synthetic: bool,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RawKeyPress {
pub(crate) logical_key: Option<LogicalKey>,
pub(crate) scan_code: u32,
pub(crate) is_down: bool,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PointerPress {
pub(crate) button: u8,
pub(crate) is_down: bool,
pub(crate) click_count: u32,
}

/// Touch event has been received
Touch(Touch),
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RawPointerPress {
pub(crate) button: u8,
pub(crate) is_down: bool,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit of disabling direct access to those fields for crate users. I feel like it should be fine and much simpler than calling getters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct field access is definitely very convenient. Getters have a couple significant advantages:

  • Adding new getters isn't a breaking change, so new data to events is very unlikely to require existing users to update their code.
  • Getters can return values that are computed from, rather than stored in, the struct. This is useful when the type of a field need to change to carry additional information, in which case the old getter can be preserved at zero cost, giving users time to switch. Although if fields can be marked deprecated, this might not be a major advantage, since one more field in an event isn't likely to be a significant cost.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding new getters isn't a breaking change, so new data to events is very unlikely to require existing users to update their code

I feel like it's related to a fact, that you'll make a constructing from fields available? The thing is that using getters in match arms is just a pain and make the code more nested and complex, however with direct access it's way easier to write things and it makes more sense in the end.

Getters can return values that are computed from, rather than stored in, the struct. This is useful when the type of a field need to change to carry additional information, in which case the old getter can be preserved at zero cost, giving users time to switch. Although if fields can be marked deprecated, this might not be a major advantage, since one more field in an event isn't likely to be a significant cost.

In general, but we're talking about events here, which you just forward to the user anyway.

Like I just don't feel like it'll make any advantage at all for the end user. You can see that this feature is very heavily used here https://github.com/alacritty/alacritty/blob/058b036fe7f3a7cdc2ebdc21ab25aded783e4e59/alacritty/src/event.rs#L573 , with getters it'll be way harder to write.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it's related to a fact, that you'll make a constructing from fields available?

I'm not sure what this means. For the same compatibility reasons, these events don't and shouldn't have public constructors.

The thing is that using getters in match arms is just a pain and make the code more nested and complex, however with direct access it's way easier to write things and it makes more sense in the end.

A side-by-side example was presented above:

WindowEvent::KeyPress { key: Some(LogicalKey::A), is_down: true, .. } => ()
WindowEvent::KeyPress(e) if e.key() == Some(LogicalKey::A) && e.is_down() => ()

The forwards-compatible version is exactly four characters longer, and I don't think it's in any sense "more nested". Further, in my experience you're only matching on exact values in toy code, so the impact of this is limited.

In general, but we're talking about events here, which you just forward to the user anyway.

Whether you're matching on a field or forwarding it elsewhere, having its name/type/presence not change when new capabilities are added reduces breakage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see that this feature is very heavily used here https://github.com/alacritty/alacritty/blob/058b036fe7f3a7cdc2ebdc21ab25aded783e4e59/alacritty/src/event.rs#L573 , with getters it'll be way harder to write.

Which feature are you referring to specifically? I don't see anything there which would obviously become much more complex with the proposed change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, and then every application create one more event to test things, which just copies winit event, except some fields they don't care about, and then you're forced to create generic abstractions over event just to support testing here or convert everything into your event adding more clones/copies.

Like alacritty is just an example here of an app, which is using winit events for testing, to just prove that it's not something uncommon.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every winit app I've ever written has immediately transformed event data into some other form, typically simple primitive values passed to a handler method. There are a variety of different approaches to consuming these events; it is absolutely not the case that literally every application would be writing a bunch of redundant code.

If you have hard data on how what proportion of winit applications do, in fact, store/pass winit events around to a significant degree, that would certainly be useful for informing the design!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By 'every application' I mean that you don't have choice anymore to do what you're doing or to do what we're doing. You just have one path - have your own event type and translate winit events.

Like ok, I personally can live with such change and we likely can refactor alacritty event handling adding even more quirks to some winit cases (though we'll likely remove one of them soon). But I'm not really sure for how long I want to workaround winit design, since it's becoming stricter and stricter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are reasonable concerns, and I've added constructors and setters for each of the event types to address those.

I'm hesitant to expose the struct fields directly, since that means that computed fields would be exposed with a different syntax than stored fields, and we're using computed fields to add an is_up() method that can be used instead of !is_down() for detecting keyup events.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm hesitant to expose the struct fields directly, since that means that computed fields would be exposed with a different syntax than stored fields, and we're using computed fields to add an is_up() method that can be used instead of !is_down() for detecting keyup events.

thx.

@Ralith
Copy link
Contributor

Ralith commented May 15, 2020

Overall those examples look good to me: forwards-compatible without unduly compromising ergonomics.

@chrisduerr
Copy link
Contributor

Besides the points noted by @kchibisov already, there are two other things that seem a bit odd to me.

I'm not really familiar with winit and Wayland (@vberger probably knows more about this), but wouldn't the removal of device ID prevent multiseat support? That seems like a bad idea.

Converting values like button from an enum to a u8 also seems like a strange choice, because at that point you're giving up some great typesystem advantages for the sake of not having to add an import? That seems very strange to me. For a boolean like is_down I can see where this idea is coming from, but for a button it just seems strange.

@Ralith
Copy link
Contributor

Ralith commented May 16, 2020

Food for thought: an enum could be compared (but not matched) against concisely and without imports by writing as u8.

@Osspial
Copy link
Contributor Author

Osspial commented May 16, 2020

I'm not really familiar with winit and Wayland (@vberger probably knows more about this), but wouldn't the removal of device ID prevent multiseat support? That seems like a bad idea.

DeviceIds and SeatIds are distinct concepts and it's misleading from an API design perspective to associate one with the other. I don't know how multiseat support is going to end up being exposed, but I don't think it's a good idea to keep DeviceIds in the API in the meanwhile when doing so poses the problems mentioned in the OP.

Converting values like button from an enum to a u8 also seems like a strange choice, because at that point you're giving up some great typesystem advantages for the sake of not having to add an import?

There's an additional reason which, looking back, I think I forgot to mention. It doesn't make sense to expose the button type as a MouseButton enum, since this overhaul unifies mouse, touch, and (in the future) pen events into a single event class, and the button field represents button state from all of those different input types. The design here was pretty heavily inspired by the web pointer event API, and we're exposing the button state in a fairly similar way to how they do it - 0 represents left-mouse-click/touch contact/pen contact, 1 represents right-mouse-click/pen barrel button, etc. That's done so that naive code written for just one pointer type automatically get a basic level support for the other pointer types.

Still, that's an entirely valid point about type safety. I've redefined the button field to take a PointerButton type here, and added is_{button} methods to allow code to inspect the button variants without importing the button type directly. How does that look to you all?

@Ralith
Copy link
Contributor

Ralith commented May 16, 2020

I rather liked the maximal concision of == 0, but it's hard to argue with the readability of named constants/predicates, especially since my guess for which integer represented "middle" was evidently wrong. I appreciate that this is forwards-compatible with larger numbers of buttons.

@Osspial
Copy link
Contributor Author

Osspial commented May 21, 2020

I've added code to ensure that the new PointerButton type can deserialize from and (optionally) serialize to the old MouseButton type, to try and minimize breakage to user config files.

@kchibisov
Copy link
Member

kchibisov commented May 21, 2020

@Osspial I can contribute Wayland impl once you've done with API on this branch, just ping me at some point, I guess.

@kchibisov kchibisov mentioned this pull request May 24, 2020
Comment on lines +1549 to +1553
// and it doesn't make a whole lot of sense for a traditional mouse
// pointer to be on a non-integer point, so round off any fractional
// values that might've cropped up.
position.x = position.x.round();
position.y = position.y.round();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't make a whole lot of sense for a traditional mouse pointer to be on a non-integer point

I think this is a mistake. High-precision mouse pointers are widely supported and are useful for a number of purposes, e.g. in art applications. See prior discussion in #1375.

Copy link
Contributor Author

@Osspial Osspial Jun 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-precision mouse pointers are typically from pen/touch devices, which this rounding does not effect. If there are any relatively widely-used devices that this change would negatively effect I'd be open to reverting this, but otherwise I think it's an appropriate action to take for standard pixel-aligned mouse devices.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Touchpads are a touch device. I'm not willing to assume without evidence that Windows always discards high-precision data from mice, either.

Copy link
Member

@kchibisov kchibisov Jun 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that touchpads are mouses actually? IIRC touchpads do have high precision data, but are treated as pointers on at least Wayland. So yeah, removing fractional part is likely not a good thing to do and I don't see much issue with it?

Also, even mouse on Wayland will have fractional part by default, so I'm not sure that things should diverge here. clients can always round themselves if they don't care, I guess.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, the terminology gets a bit confusing here. I mean that touchpads are high-precision input devices driven by a finger which can, should, and on non-Windows backends do have that precision propagated to the application. Even conventional mice can be, contrary to the comment above.

There's no reason a conventional mouse should be treated differently either, as you observe. Silently discarding perfectly good data is bad behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

although it's not really useful in the windowing system with a standard pointer

I'd like to emphasize that high-precision pointers absolutely can be useful. For example, any time you're interacting with a vector or zoomed-out raster graphics canvas, or operating a continuous UI control like a slider.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or operating a continuous UI control like a slider

Ah, true there, I have actually noticed that on some KDE sliders where I can barely adjust them but the cursor doesn't actually move...

Copy link
Contributor Author

@Osspial Osspial Jun 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Touchpads are a touch device. I'm not willing to assume without evidence that Windows always discards high-precision data from mice, either.

I have a laptop that uses the Windows Precision Touchpad drivers. When using the touchpad, Windows does not dispatch sub-pixel pointer movement events, and each movement event's position is within a +-0.05 margin of an integer pixel number. WRT mice - I have a Logitech G502 gaming mouse, and Windows exhibits the same behavior there. This is a rounding error, and should be treated as such.

My mouse, standard (very high resolution) mouse also sends fractional parts when moving, and although it's not really useful in the windowing system with a standard pointer, it's very useful in games that support them.

Are you on a Windows system? If so, could you check whether or not event-polish with this diff applied emits subpixel mouse movement events in the window example?

diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs
index 3702d352..b1d7ae8c 100644
--- a/src/platform_impl/windows/event_loop.rs
+++ b/src/platform_impl/windows/event_loop.rs
@@ -1549,8 +1549,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
                             // and it doesn't make a whole lot of sense for a traditional mouse
                             // pointer to be on a non-integer point, so round off any fractional
                             // values that might've cropped up.
-                            position.x = position.x.round();
-                            position.y = position.y.round();
+                            // position.x = position.x.round();
+                            // position.y = position.y.round();
 
                             buttons_down.set(
                                 PointerButton::MOUSE_LEFT.into_flags(),

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is ultimately determined that windows genuinely never reports non-integer mouse/touchpad coordinates, then at the very least the comment should reflect that, rather than claiming there's no use case for higher precision data. That way if a future change makes higher-precision data available there won't be cause for confusion as to whether it should be discarded.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you on a Windows system?

I'm not, linux with a custom mouse driver because my mouse is... unique.

Comment on lines +174 to +185
PointerCreated(PointerId),
PointerEntered(PointerId),
PointerForce(PointerId, Force),
PointerTilt(PointerId, PointerTiltEvent),
PointerTwist(PointerId, f64),
PointerContactArea(PointerId, PhysicalSize<f64>),
PointerMoved(PointerId, PhysicalPosition<f64>),
// TODO: IMPLEMENT. IS THIS REASONABLE TO IMPLEMENT ON NON-WINDOWS PLATFORMS?
// PointerHovered(PointerId),
PointerButton(PointerId, PointerButtonEvent),
PointerDestroyed(PointerId),
PointerLeft(PointerId),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These fields lack documentation. Do they work as follows?

  • Create a pointer corresponding to the mouse on app start
  • Create a pointer corresponding to a touch (with unique ID) when the touch starts and destroy when the touch ends
  • Send PointerButton events just after creating and before destroying every touch event
  • The same for a pen, if it exists (since in general one can't provide PointerMoved events for a pen not touching the screen)

For motion events, presumably the app must remember the last position for each pointer if it wishes to know relative motion? As you see here I ended up providing both absolute and relative positions for motion events in my unified API (but this API is for higher-level usage and misses some of the details).

How do the pen events (force, tilt, twist) work for sensitive pens? Do they tend to send all of these events each frame? If so it may be more convenient to receive all this data simultaneously. (But I'm only guessing since I haven't worked with pens.)

@OvermindDL1
Copy link

A curiosity, does this give an ID to mice as well? I.E. would multiple mice (on system types that support it like X11 or wayland, or windows with an admin callback hook) be able to distinguish the different mice? I exceptionally hate having to bypass windowing mice handling to be able to support this feature so it would be nice to actually be built in for once.

@dhardy
Copy link
Contributor

dhardy commented Jun 30, 2020

@OvermindDL1 it looks like it, given that a PointerId may be a MouseId, which is a platform-defined type (only the Windows variant is implemented here, and that's a unit type, but on X11 it may not be). I am curious how this works with the app though — does winit synthesise a PointerCreated event for each mouse? Is there an easily identifiable "primary pointer"? And how useful is multiple-pointers-per-display anyway (it's such an obscure feature I would guess it's also buggy)?

@OvermindDL1
Copy link

And how useful is multiple-pointers-per-display anyway

If a program isn't built to handle them then they usually just handle the 'first' mouse or just kind of combine all their inputs together (bleh). For programs that are aware of it as I try for mine to be then they work quite well and has some very useful usecases even outside of games.

@dhardy
Copy link
Contributor

dhardy commented Jun 30, 2020

Multi-mouse may not be exclusive to Linux either (though this sounds limited): https://www.mousemux.com/

@kchibisov
Copy link
Member

It's not that buggy on Wayland, but I'm not sure that it's that supported across compositors, in general, multiseat(aka multiple keyboard + pointers) is a thing. winit side here will be simple (when it comes to Wayland at least), since everything is already handed by you, and you know that you, let's say, have multiple pointers, if you have multiple seats with pointer capability. The pointer/keyboard event are per seat, so the only thing where winit should likely do a bit more work here, is 'proper id' for them(not sure how it'll work).

@OvermindDL1
Copy link

OvermindDL1 commented Jun 30, 2020

Plus you want a good DE that's supporting it well as well, otherwise yes, as like on the link it can cause flickering of which program has mouse 'activity'. KDE used to be the best supported, wonder if it still is... Although if both mice are being used in the same program then that's not an issue anyway.

@dhardy
Copy link
Contributor

dhardy commented Jun 30, 2020

Just tested on X11 + KDE: it works, but the second cursor causes horrible flickering. Additionally, it appears the keyboard should only follow the first pointer, but the second can shift keyboard focus within most apps, and sometimes it even gets stuck in the wrong app.

Conclusion: it's horribly buggy, as expected, and I doubt it will ever get fixed properly (it's old and thus far remained a barely known niche feature), thus I see little point caring.

@OvermindDL1
Copy link

OvermindDL1 commented Jun 30, 2020

thus I see little point caring.

This is the reason why the support is spotty between apps. Even still, within a single app they work perfectly, and this is my uses for it.

@DorianRudolph
Copy link

I implemented stylus support for X11 which might be helpful in porting this to X11 (it looks like only windows is implemented right now) https://github.com/DorianRudolph/winit/tree/stylus

@madsmtm madsmtm mentioned this pull request Jan 13, 2022
5 tasks
@kchibisov kchibisov mentioned this pull request Aug 4, 2023
5 tasks
@0x182d4454fb211940
Copy link

Hi can I ask what the state of this pr is? I'm interested in using winit in a stylus-based project and so I'd be willing to help with this if needed. I tried suggesting an API in #3001 but this looks more comprehensive.

@kchibisov
Copy link
Member

I could say for sure that it's unlikely to get rebased 0x182d4454fb211940 , but you could use it as basis to sketch API.

@kchibisov
Copy link
Member

#3876 took care of it, to some extent.

@kchibisov kchibisov closed this Oct 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S - api Design and usability
Development

Successfully merging this pull request may close these issues.