Git Product home page Git Product logo

winit_input_helper's Introduction

Winit Input Helper

Crates.io Docs

Processes and stores winit events, allowing input state to be queried at any time.

How to use

Each event is passed to the WinitInputHelper via the update method.

The current input state can then be accessed via methods such as key_pressed, key_released, key_held, mouse, mouse_diff etc.

To see all available methods look at docs.rs

use winit::event_loop::EventLoop;
use winit::keyboard::{Key, KeyCode};
use winit::window::WindowBuilder;
use winit_input_helper::WinitInputHelper;

fn main() {
    let mut input = WinitInputHelper::new();

    let event_loop = EventLoop::new().unwrap();
    let _window = WindowBuilder::new().build(&event_loop).unwrap();

    event_loop
        .run(move |event, elwt| {
            // Pass every event to the WinitInputHelper.
            // It will return true when the last event has been processed and it is time to run your application logic.
            if input.update(&event) {
                if input.key_released(KeyCode::KeyQ) || input.close_requested() || input.destroyed()
                {
                    elwt.exit();
                    return;
                }

                if input.key_pressed(KeyCode::KeyW) {
                    println!("The 'W' key (US layout) was pressed on the keyboard");
                }

                if input.key_held(KeyCode::KeyR) {
                    println!("The 'R' key (US layout) key is held");
                }

                // query the change in cursor this update
                let cursor_diff = input.cursor_diff();
                if cursor_diff != (0.0, 0.0) {
                    println!("The cursor diff is: {:?}", cursor_diff);
                    println!("The cursor position is: {:?}", input.cursor());
                }

                // You are expected to control your own timing within this block.
                // Usually via rendering with vsync.
                // render();
            }
        })
        .unwrap();
}

Publishing a new version

In order to avoid forcing the user to enable the default winit backends, winit_input_helper sets its winit dependency to default-features = false. This complicates the publishing procedure a little because winit cannot compile without any backends enabled.

So to publish we run: cargo publish --features winit/default

winit_input_helper's People

Contributors

azkellas avatar ekardnt avatar emmabritton avatar gingeh avatar lukaskalbertodt avatar parasyte avatar rukai avatar schnippl0r avatar thebutlah avatar zstorm999 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

winit_input_helper's Issues

Ignore duplicate KeyboardInput events

I haven't looked into this too much, but at least on Windows 11, winit sends repeated KeyboardInput events while a key is held. These repeated events will always push KeyAction::Pressed(_), causing key_pressed() to return true at the same interval as the repeat events.

ElementState::Pressed => {
self.key_held[keycode as usize] = true;
self.key_actions.push(KeyAction::Pressed(keycode));
if let VirtualKeyCode::Back = keycode {
self.text.push(TextChar::Back);
}
}

IMHO, this code should only push the KeyAction when self.key_held[keycode as usize] == false (i.e. the key has not been released since the last pressed event).

CloseRequested from windows task bar is not propagated

Clicking on "Close window" via the windows task bar is ignored in winit_input_helper.
The CloseRequested event is sent between two steps, after an AboutToWait and before a NewEvents(_), so when the NewEvents(_) arrives, the step begins and reset WinitInputHelper::close_requested to false.

With ControlFlow::Poll:

event: NewEvents(Poll)
event: AboutToWait
event: WindowEvent { window_id: WindowId(WindowId(11996930)), event: CloseRequested }
event: WindowEvent { window_id: WindowId(WindowId(11996930)), event: Focused(true) }
event: NewEvents(Poll)
event: AboutToWait

Without ControlFlow::Poll:

event: AboutToWait
event: NewEvents(WaitCancelled { start: Instant { t: 1776733.8859361s }, requested_resume: None })
event: AboutToWait
event: WindowEvent { window_id: WindowId(WindowId(340828)), event: Focused(true) }
event: WindowEvent { window_id: WindowId(WindowId(340828)), event: CloseRequested }
event: NewEvents(WaitCancelled { start: Instant { t: 1776734.1857145s }, requested_resume: None })
event: AboutToWait

improve mouse_diff interaction with step_with_window_events

Currently using step_with_window_events will never update mouse_diff as it relies on DeviceEvents but it only takes WindowEvents.

Possibly the solution is to update step_with_window_events to take Event instead of WindowEvent, but I will need to investigate how that affects usage of the API first.

Use `DeviceEvent::MouseMotion` instead of `WindowEvent::CursorMoved` for `mouse_diff()`

In #12 , @rukai suggested that we replace WindowEvent::CursorMoved by DeviceEvent::MouseMotion instead of having two separate mouse functions.

This was because MouseMotion is a more useful event, since it works even when the cursor is locked.

There were a few PRs, chiefly #13 and #20 that implemented a split, but none so far have implemented a replacement as originally suggested. I am happy to work on replacing the event and will submit a PR soon.

Differentiate between mouse_diff and cursor_diff values

Currently, your mouse_diff api depends WindowEvent::CursorMoved event. This is confusing as winit offers a DeviceEvent::MouseMotion event that shows whether the mouse has moved, in contrast with the WindowEvent::CursorMoved which shows whether the cursor has moved.

With this in mind, you should have 2 relevant functions: mouse_diff (that uses DeviceEvent::MouseMotion event) and cursor_diff (that uses WindowEvent::CursorMoved event). The first one will always trigger when moving the mouse, while the second one will trigger only the cursor moves. This will allow the users to build things like first person cameras that have a potential infinite mouse movement when grabing the cursor in the window.

This is not currently possible with the mouse_diff api, because even when grabing the cursor in the window, the cursor at some point reaches the window edge and it stops generating mouse diff events.

Support for scancodes

Hello !

I have a non-english keyboard (french bépo) with characters that are not represented as VirtualKeyCodes for obvious reasons.

Is there a plan to update this library to support access to the scancodes instead of just the VirtualKeyCodes ?

For now keys that do not correspond to a virtual key return None when accessing input.virtual_keycode, and this behaviour is simply skipped at the time. I believe this could be fixed relatively simply, by adding a tracking of the scancode at the same time as the virtual_keycode.

Cancel quit?

When the quit boolean is set to true, it is permanently true. Since it can't be reset, I'm not able to use it for close requests.

I want to prompt the user to confirm the close request when there is unsaved progress. It's a game, but you do see the same UX expectation with many applications.

There is also an issue with not being able to tell the difference between a close request and the window getting destroyed:

WindowEvent::CloseRequested | WindowEvent::Destroyed => self.quit = true,

I am working around it by handling these events myself.

Pairing with glutin and glium

Trying to use this package with a glutin/glium spawned "Event" struct produces an error.

"^^^^^^ expected enum winit::event::Event, found enum glium::glutin::event::Event"

This is seemingly because winit_input_helper currently uses winit 0.22.2 as a dependency. Is it likely for this to be the problem, and if so can I even revert winit_input_helper's winit version through cargo.toml?

Reexport winit features

It's often desirable to reduce the winit feature set depending on your platform and needs, and it can reduce the binary size. But to using winit_input_helper negates that since it uses the default feature set. By re-exporting the features, it should be possible to only keep one winit version in the binaries.

A way to trigger on chorded input? (e.g. `C-x q`)

Hi,

I like how this crate simplifies a lot of the logic in winit's input matching system-- was wondering if this crate had a way to trigger on chorded key commands (CTRL-X and then followed by another letter like Q).

I'm a little new to rust and the ecosystem, but if this is not currently possible, I was thinking of creating some sort of queue that tracks key combinations being pressed and trigger based on that. Do you think that would work or/and be a performant technique?

Any insight would be appreciated!

Could not compile 0.4.0-alpha6 on Windows (Rust 1.40.0)

When running cargo build on my Windows 10 computer for my project using Rust 1.40.0, I am receiving the following error:

   Compiling winit_input_helper v0.4.0-alpha6
   Compiling wgpu-native v0.4.2
error[E0106]: missing lifetime specifier
  --> \.cargo\registry\src\github.com-1ecc6299db9ec823\winit_input_helper-0.4.0-alpha6\src\lib.rs:17:25
   |
17 |     events:         Vec<Event<T>>,
   |                         ^^^^^^^^ expected lifetime parameter

error[E0107]: wrong number of type arguments: expected 1, found 0
  --> \.cargo\registry\src\github.com-1ecc6299db9ec823\winit_input_helper-0.4.0-alpha6\src\lib.rs:14:28
   |
14 |     window_resized: Option<LogicalSize>,
   |                            ^^^^^^^^^^^ expected 1 type argument

error[E0107]: wrong number of type arguments: expected 1, found 0
   --> \.cargo\registry\src\github.com-1ecc6299db9ec823\winit_input_helper-0.4.0-alpha6\src\lib.rs:246:44
    |
246 |     pub fn window_resized(&self) -> Option<LogicalSize> {
    |                                            ^^^^^^^^^^^ expected 1 type argument

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0106, E0107.
For more information about an error, try `rustc --explain E0106`.
error: could not compile `winit_input_helper`.

Rustup output:

stable-x86_64-pc-windows-msvc (default)
rustc 1.40.0 (73528e339 2019-12-16)

The same code works on a Mac running Catalina and Rust 1.40.0.

Update to winit 0.30

Winit recently released a new version, where they overhauled the preferred way of doing the event loop. Would it be possible to get an updated version of this lovely library?

I also took a look at how much effort this would be:

`input.mouse` does not return `None` outside the window

When dragging the mouse, and checking its position in the code with input.mouse_held(...) and input.mouse(), the position is not reported as None even when the cursor is dragged outside the window. Instead, I get a negative or out of bounds position.

Here is a minimal working example :

use winit::event_loop::EventLoop;
use winit::window::WindowBuilder;
use winit_input_helper::WinitInputHelper;

fn main() {
    let mut input = WinitInputHelper::new();

    let event_loop = EventLoop::new();
    let _window = WindowBuilder::new().build(&event_loop).unwrap();

    event_loop.run(move |event, _, _| {
        if input.update(&event) {
            if input.mouse_held(0) {
                println!("mouse at {:?}", input.mouse())
            }
        }
    });
}

This can return for example (when dragging cursor to the left of the window)

mouse at Some((-43.0, 517.0))

I’m currently on Windows 10, maybe this doesn’t apply to other operating systems/window managers.

From the documentation of input.mouse, I would expect this to return None when the mouse is outside the window. This could be fixed by using WindowEvent::CursorEntered and WindowEvent::CursorLeft events to keep track of the mouse being on the window.

Support for using DeviceEvent

Would it be possible to add support to switch between using WindowEvent and DeviceEvent, because some events are suited for games controls and some aren't

No Meta/Logo modifier

I have two issues to report, but they belong together.

Background

In my event loop I have something like this:

 if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
    *control_flow = ControlFlow::Exit;
    return;
}

Which is to say, I want the escape key to act as a shutdown to my software.


Problem 1 - Modifiers and my Window Manager

I run linux and use a non-standard window manager. I have a shortcut, when I hit the Meta/Logo key and escape, it switches focus to my last window. This kills my Winit program according to the code snippet above. I understand why, but I think it should not. The Meta/Logo key as a modifier should be special.

Problem 2 - No ModifierState helper for Meta/Logo

This crate is missing a helper around the winit::event::ModifiersState::logo function. I see all the others:

    /// Returns true while any shift key is held on the keyboard.
    /// Otherwise returns false.
    pub fn held_shift(&self) -> bool {
        self.key_held(VirtualKeyCode::LShift) || self.key_held(VirtualKeyCode::RShift)
    }

    /// Returns true while any control key is held on the keyboard.
    /// Otherwise returns false.
    pub fn held_control(&self) -> bool {
        self.key_held(VirtualKeyCode::LControl) || self.key_held(VirtualKeyCode::RControl)
    }

    /// Returns true while any alt key is held on the keyboard.
    /// Otherwise returns false.
    pub fn held_alt(&self) -> bool {
        self.key_held(VirtualKeyCode::LAlt) || self.key_held(VirtualKeyCode::RAlt)
    }

but not for the Meta/Logo modifier state.

Proposal

My window manager lets me define not only keys, but modifiers when registering keybindings. I think a change like this may be good.

enum Modifier {
    Ctrl,
    Alt,
    Shift,
    Logo,
}
enum KeyModifier {
    None,
    Single(Modifier)
    Multi(Vec<Modifier>),
}

input.key(KeyModifier::None, VirtualKeyCode::Escape) ...
input.key(KeyModifier::Single(Modifier::Alt), VirtualKeyCode::F4) ...
input.key(KeyModifier::Multi(vec![Modifier::Alt, Modifier::Shift]), VirtualKeyCode::C) ...

Which is a slightly higher-level API than the one you currently support. It will be more explicit - that first example for an Escape key with no modifiers will no longer trigger when I hit Mod-Esc.


I've been using your library for a while, I'm happy to contribute a PR if this is something you want to add.

TextChar::Char is never constructed

I tried using .text(), but no TextChar::Chars are ever yielded. Looking at the code these don't seem to be constructed anywhere? I assume this is just an oversight, so I made an issue

Why isn't this printing `worked?`

event_loop.run(move |event, _, control_flow| {
    ...

    if Instant::now().duration_since(frame_time) >= SIXTY_FPS
        && *control_flow != ControlFlow::Exit
    {
        frame_time = Instant::now();

        if input.update(&event) {
            println!("womens");
            if input.key_pressed(VirtualKeyCode::W) {
                println!("worked?");
                vertices[0].position[1] += 1.0;
            }
        }

        ...
    }
});``` This code prints `womens` but it does not print `worked?`, am I doing something wrong or is it related to other things?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.