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

Input rebind patch #247

Merged
merged 7 commits into from Jun 19, 2017

Conversation

Projects
None yet
6 participants
@Xaeroxe
Member

Xaeroxe commented Jun 17, 2017

I'm working on enhancements to the input system and adding support for key rebinding. I'm opening this pull request now to start gathering feedback.

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 17, 2017

Member

The only file modified in any meaningful way in this PR so far is src/ecs/resources​/input.rs everything else is just whitespace changes.

Whitespace changes have been removed.

Member

Xaeroxe commented Jun 17, 2017

The only file modified in any meaningful way in this PR so far is src/ecs/resources​/input.rs everything else is just whitespace changes.

Whitespace changes have been removed.

@torkleyy

Looks nice, here just a few first thoughts.

src/ecs/resources/input.rs
@@ -62,45 +80,63 @@ impl<'a> Iterator for PressedMouseButtons<'a> {
/// ```
#[derive(Default)]
pub struct InputHandler {
- pressed_keys: HashMap<VirtualKeyCode, KeyQueryState>,
- pressed_mouse_buttons: HashMap<MouseButton, KeyQueryState>,
+ pressed_keys: Vec<VirtualKeyCode>,

This comment has been minimized.

@torkleyy

torkleyy Jun 17, 2017

Member

It would be great if we could instead just have a boolean Vec where the index is the key. Also see this issue.

@torkleyy

torkleyy Jun 17, 2017

Member

It would be great if we could instead just have a boolean Vec where the index is the key. Also see this issue.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

I had similar thoughts, I initially wanted to store a fixed size array of bools as part of the struct, where each VirtualKeyCode (or MouseButton) as usize is the index into the array. This reduces the amount of times we have to follow pointers and do allocations, but determining what that fixed size should be is a bit hard as there isn't an easy way to get a count of all the variants of a VirtualKeyCode. I could just hard code a value but this makes for some brittle code if new codes are ever added. Additionally every frame the current input state needs to be copied to the previous input state as well, so if the input state structures are larger this gets more expensive. There are also limitations on how large this Vec<VirtualKeyCode> can get, most of the time it will have < 5 elements, so iterating it to determine key state isn't that expensive, especially when compared to the cost of copying an entire Vec<bool> of all keys every frame.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

I had similar thoughts, I initially wanted to store a fixed size array of bools as part of the struct, where each VirtualKeyCode (or MouseButton) as usize is the index into the array. This reduces the amount of times we have to follow pointers and do allocations, but determining what that fixed size should be is a bit hard as there isn't an easy way to get a count of all the variants of a VirtualKeyCode. I could just hard code a value but this makes for some brittle code if new codes are ever added. Additionally every frame the current input state needs to be copied to the previous input state as well, so if the input state structures are larger this gets more expensive. There are also limitations on how large this Vec<VirtualKeyCode> can get, most of the time it will have < 5 elements, so iterating it to determine key state isn't that expensive, especially when compared to the cost of copying an entire Vec<bool> of all keys every frame.

src/ecs/resources/input.rs
+ }
+}
+
+fn mouse_button_to_button(mb: &MouseButton) -> Button {

This comment has been minimized.

@torkleyy

torkleyy Jun 17, 2017

Member

Could we have that as an Into implementation?

@torkleyy

torkleyy Jun 17, 2017

Member

Could we have that as an Into implementation?

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Sure! I'll make one.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Sure! I'll make one.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

It's been added!

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

It's been added!

src/ecs/resources/input.rs
+ }
+
+ /// Returns the value of an axis by the i32 id, if the id doesn't exist this returns None.
+ pub fn axis_value(&self, id: i32) -> Option<f32> {

This comment has been minimized.

@torkleyy

torkleyy Jun 17, 2017

Member

I see, so this is how you would refer to an axis defined in the config file?

@torkleyy

torkleyy Jun 17, 2017

Member

I see, so this is how you would refer to an axis defined in the config file?

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Eventually yes, I haven't quite fleshed out the rebinding part yet

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Eventually yes, I haven't quite fleshed out the rebinding part yet

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 17, 2017

Member

This PR is as finished as it can be until #244 is merged.

Changelog entry:

  • Add function text_entered which returns a &str of characters sent to the game window during the current frame, useful for in game text entry fields.
  • Add _released functions to detect when an input is released
  • All _down functions have been renamed to _pressed
  • All _once functions have been renamed to _down, and they all will report the correct values even if they've already been called once on this frame.
  • Button type has been added, which is an enum containing the VirtualKeyCode and MouseButton types
  • Functions to compliment Button have been added,
    • pub fn pressed_buttons(&self) -> PressedButtons
    • pub fn button_pressed(&self, button: Button) -> bool
    • pub fn buttons_pressed(&self, buttons: &[Button]) -> bool
    • pub fn button_down(&self, button: Button) -> bool
    • pub fn button_released(&self, button: Button) -> bool
    • pub fn buttons_down(&self, buttons: &[Button]) -> bool
  • Actions have been added, which are mappings between Buttons and i32 values, allowing for rebinding of inputs.
  • Similar functions for actions were added, with the exception that there is no pressed_actions. The function was difficult to write and I couldn't think of a use case for it.
  • Axis struct added, which is a mapping of twoButtons, one positive and one negative.
  • Axes can be mapped to i32s on the InputHandler and then queried for with the axis_value function.
Member

Xaeroxe commented Jun 17, 2017

This PR is as finished as it can be until #244 is merged.

Changelog entry:

  • Add function text_entered which returns a &str of characters sent to the game window during the current frame, useful for in game text entry fields.
  • Add _released functions to detect when an input is released
  • All _down functions have been renamed to _pressed
  • All _once functions have been renamed to _down, and they all will report the correct values even if they've already been called once on this frame.
  • Button type has been added, which is an enum containing the VirtualKeyCode and MouseButton types
  • Functions to compliment Button have been added,
    • pub fn pressed_buttons(&self) -> PressedButtons
    • pub fn button_pressed(&self, button: Button) -> bool
    • pub fn buttons_pressed(&self, buttons: &[Button]) -> bool
    • pub fn button_down(&self, button: Button) -> bool
    • pub fn button_released(&self, button: Button) -> bool
    • pub fn buttons_down(&self, buttons: &[Button]) -> bool
  • Actions have been added, which are mappings between Buttons and i32 values, allowing for rebinding of inputs.
  • Similar functions for actions were added, with the exception that there is no pressed_actions. The function was difficult to write and I couldn't think of a use case for it.
  • Axis struct added, which is a mapping of twoButtons, one positive and one negative.
  • Axes can be mapped to i32s on the InputHandler and then queried for with the axis_value function.

@Xaeroxe Xaeroxe changed the title from [WIP] Input rebind patch to Input rebind patch Jun 17, 2017

@kvark kvark referenced this pull request in three-rs/three Jun 17, 2017

Closed

Input format #8

src/ecs/resources/input.rs
+ //TODO: Add controller buttons here when the engine has support.
+}
+
+impl Into<Button> for VirtualKeyCode {

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

I think it's more idiomatic to have impl From<VirtualKeyCode> for Button

@kvark

kvark Jun 17, 2017

Member

I think it's more idiomatic to have impl From<VirtualKeyCode> for Button

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Ah you're right, thanks.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Ah you're right, thanks.

src/asset_manager/asset_manager.rs
- }
- None => [0.0, 0.0],
- },
+ geometry

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

I don't think mixing your input PR with rustfmt pass is a good idea. Better have them separate. FWIW, I like the old formatting better.

@kvark

kvark Jun 17, 2017

Member

I don't think mixing your input PR with rustfmt pass is a good idea. Better have them separate. FWIW, I like the old formatting better.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

I asked @ebkalderon about mixing this with rustfmt and he didn't seem to mind, however in this specific instance I just realized it's very likely to have a merge conflict with #244 and I'd rather not have that. So I'll remove the other files from this PR.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

I asked @ebkalderon about mixing this with rustfmt and he didn't seem to mind, however in this specific instance I just realized it's very likely to have a merge conflict with #244 and I'd rather not have that. So I'll remove the other files from this PR.

@@ -28,7 +41,7 @@ impl<'a> Iterator for PressedKeys<'a> {
/// An iterator over the currently pressed down mouse buttons.
pub struct PressedMouseButtons<'a> {
- iterator: Keys<'a, MouseButton, KeyQueryState>,
+ iterator: Iter<'a, MouseButton>,

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

is there any benefit over doing just type PressedMouseButtons<'a> = Iter<'a, MouseButton>?

@kvark

kvark Jun 17, 2017

Member

is there any benefit over doing just type PressedMouseButtons<'a> = Iter<'a, MouseButton>?

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

This conceals the implementation details a bit better, but other than that no not really. When I opened the file it was a wrapper struct so I left it that way. I'm happy to change it either way.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

This conceals the implementation details a bit better, but other than that no not really. When I opened the file it was a wrapper struct so I left it that way. I'm happy to change it either way.

src/ecs/resources/input.rs
// defined to match glium
mouse_position: Option<(i32, i32)>,
previous_mouse_position: Option<(i32, i32)>,
+ axes: HashMap<i32, Axis>,

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

I assume these i32 are specific indices of the elements, in which case it would be a good idea to make them opaque (i.e. struct AxeId(i32)) so that the user is not able to change them

@kvark

kvark Jun 17, 2017

Member

I assume these i32 are specific indices of the elements, in which case it would be a good idea to make them opaque (i.e. struct AxeId(i32)) so that the user is not able to change them

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Good note, I'll do that.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Good note, I'll do that.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 18, 2017

Member

On second thought it'd probably be better for this to be transparent right now, as the facilities for making input rebinding part of the asset system don't exist yet. I'll probably see if I can do this again when #244 is merged.

@Xaeroxe

Xaeroxe Jun 18, 2017

Member

On second thought it'd probably be better for this to be transparent right now, as the facilities for making input rebinding part of the asset system don't exist yet. I'll probably see if I can do this again when #244 is merged.

src/ecs/resources/input.rs
+ // Before processing these events store the input states of the previous frame.
+ self.previous_mouse_position = self.mouse_position;
+ self.previous_pressed_keys = self.pressed_keys.clone();
+ self.previous_pressed_mouse_buttons = self.pressed_mouse_buttons.clone();

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

nit: should re-use the existing allocation instead of cloning, i.e.

vector.clear();
vector.extend_from_slice(&other_vector);
@kvark

kvark Jun 17, 2017

Member

nit: should re-use the existing allocation instead of cloning, i.e.

vector.clear();
vector.extend_from_slice(&other_vector);

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

0_o TIL a cool new trick, thanks!

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

0_o TIL a cool new trick, thanks!

src/ecs/resources/input.rs
- // I.e `key_once(key)` is queried after second `Pressed` event.
- if let Entry::Vacant(entry) = self.pressed_keys.entry(key_code) {
- entry.insert(KeyQueryState::NotQueried);
+ if self.pressed_keys.iter().all(|&k| k != key_code) {

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

I wonder if a HashSet would be more convenient here

@kvark

kvark Jun 17, 2017

Member

I wonder if a HashSet would be more convenient here

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

It might be, although I think the performance of Vec might actually be better as iterating over <5 elements in contiguous memory is probably faster than any hashing function

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

It might be, although I think the performance of Vec might actually be better as iterating over <5 elements in contiguous memory is probably faster than any hashing function

This comment has been minimized.

@kvark

kvark Jun 18, 2017

Member

Right, but I don't think it's even worth optimizing if there are just 5 keys. HashSet is just more convenient.

@kvark

kvark Jun 18, 2017

Member

Right, but I don't think it's even worth optimizing if there are just 5 keys. HashSet is just more convenient.

src/ecs/resources/input.rs
+ pub fn actions_pressed(&self, actions: &[i32]) -> Result<bool, Vec<i32>> {
+ let mut all_buttons_pressed = true;
+ //Prefer to skip initializing a new vec if we can.
+ let mut bad_values: Option<Vec<i32>> = None;

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

Vec::new() costs absolutely nothing, no need to wrap it into Option

@kvark

kvark Jun 17, 2017

Member

Vec::new() costs absolutely nothing, no need to wrap it into Option

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Cool! Thanks!

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Cool! Thanks!

src/ecs/resources/input.rs
+ }
+ }
+ if let Some(values) = bad_values {
+ return Err(values);

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

no need for return statements here

@kvark

kvark Jun 17, 2017

Member

no need for return statements here

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Yeah I guess it's more of a stylistic choice. I'll remove them though.

@Xaeroxe

Xaeroxe Jun 17, 2017

Member

Yeah I guess it's more of a stylistic choice. I'll remove them though.

src/ecs/resources/input.rs
+ let mut all_buttons_pressed = true;
+ let mut any_button_pressed_this_frame = false;
+ //Prefer to skip initializing a new vec if we can.
+ let mut bad_values: Option<Vec<i32>> = None;

This comment has been minimized.

@kvark

kvark Jun 17, 2017

Member

same here as above

@kvark

kvark Jun 17, 2017

Member

same here as above

@kvark

kvark approved these changes Jun 18, 2017

@kvark

This comment has been minimized.

Show comment
Hide comment
@kvark

kvark Jun 18, 2017

Member

I don't think HashSet is essential here (we can change it later, it's an impl detail anyway), so I'd be ready to merge this. Unless there is something else you want to do in the PR, or other reviewers want to chime in.

Member

kvark commented Jun 18, 2017

I don't think HashSet is essential here (we can change it later, it's an impl detail anyway), so I'd be ready to merge this. Unless there is something else you want to do in the PR, or other reviewers want to chime in.

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

As of this moment I consider this merge ready. If any other reviewers want to chime in I'll be happy to hear what they have to say.

Member

Xaeroxe commented Jun 18, 2017

As of this moment I consider this merge ready. If any other reviewers want to chime in I'll be happy to hear what they have to say.

@Aceeri

Aceeri approved these changes Jun 18, 2017 edited

Looks good to me. Would this also give the benefit of keys pressed in a specific order? As in you could have something based on whether A->B was pressed or if B->A was pressed or would that depend more on glutin's input handling?

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

@Aceeri This API doesn't store key press ordering in any way, in order to accomplish that you'd need some kind of state machine, which could be powered by the functions given here but no this won't handle the order in which keys are pressed. I'd recommend storing that information in the state of your components, as it's probably much better suited to that in my opinion. That way you can also have your visuals and audio respond to keys being pressed in a specific order. For example:

Press A -> UI icon flashes and changes what image is displayed, accompanied by some kind of "acknowledged" sound effect
Press B -> Action executes, UI icon switches back

Press B -> UI icon flashes and changes to a different image than it would if A were pressed, also accompanied by some kind of "acknowledged" sound effect
Press A -> Action executes, UI icon switches back

Member

Xaeroxe commented Jun 18, 2017

@Aceeri This API doesn't store key press ordering in any way, in order to accomplish that you'd need some kind of state machine, which could be powered by the functions given here but no this won't handle the order in which keys are pressed. I'd recommend storing that information in the state of your components, as it's probably much better suited to that in my opinion. That way you can also have your visuals and audio respond to keys being pressed in a specific order. For example:

Press A -> UI icon flashes and changes what image is displayed, accompanied by some kind of "acknowledged" sound effect
Press B -> Action executes, UI icon switches back

Press B -> UI icon flashes and changes to a different image than it would if A were pressed, also accompanied by some kind of "acknowledged" sound effect
Press A -> Action executes, UI icon switches back

@torkleyy

This comment has been minimized.

Show comment
Hide comment
@torkleyy

torkleyy Jun 18, 2017

Member

@Xaeroxe How should we handle text input (e.g. for an in-game chat)?

Member

torkleyy commented Jun 18, 2017

@Xaeroxe How should we handle text input (e.g. for an in-game chat)?

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

@torkleyy That's a great question. This API is awful for that currently. I'd like to change that. Please don't merge until I have that in.

Member

Xaeroxe commented Jun 18, 2017

@torkleyy That's a great question. This API is awful for that currently. I'd like to change that. Please don't merge until I have that in.

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

@torkleyy function text_entered has been added, which returns a &str of all characters sent to the window during the current frame, you can insert the output of that function wherever your cursor is in your text entry field.

I consider this ready to merge again.

Member

Xaeroxe commented Jun 18, 2017

@torkleyy function text_entered has been added, which returns a &str of all characters sent to the window during the current frame, you can insert the output of that function wherever your cursor is in your text entry field.

I consider this ready to merge again.

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

Also as an aside: This won't handle for backspace and delete (backspace and delete "characters" will likely be directly in the output of text_entered). Full text entry support is more of a UI problem and in my opinion slightly out of scope for this, but at least now we have the facilities to add full text entry support at a later date.

Member

Xaeroxe commented Jun 18, 2017

Also as an aside: This won't handle for backspace and delete (backspace and delete "characters" will likely be directly in the output of text_entered). Full text entry support is more of a UI problem and in my opinion slightly out of scope for this, but at least now we have the facilities to add full text entry support at a later date.

src/ecs/resources/input.rs
}
impl InputHandler {
/// Creates a new input handler.
pub fn new() -> InputHandler {
InputHandler {
- pressed_keys: HashMap::default(),
- pressed_mouse_buttons: HashMap::default(),
+ pressed_keys: Vec::new(),

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

Abstracting pressed_* and previous_* into a separate struct could reduce code duplication.

@msiglreith

msiglreith Jun 18, 2017

Contributor

Abstracting pressed_* and previous_* into a separate struct could reduce code duplication.

src/ecs/resources/input.rs
}
/// Checks if all the given keys are being pressed.
- pub fn keys_down(&self, keys: &[VirtualKeyCode]) -> bool {
+ pub fn keys_pressed(&self, keys: &[VirtualKeyCode]) -> bool {

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

keys_pressed and key_pressed seem to have a different behavior as the first one uses key_down internally.

@msiglreith

msiglreith Jun 18, 2017

Contributor

keys_pressed and key_pressed seem to have a different behavior as the first one uses key_down internally.

src/ecs/resources/input.rs
- keys.iter().any(|key| self.key_once(*key)) && self.keys_down(keys)
+ /// Checks if the all the given keys are down and at least one was pressed on this frame.
+ pub fn keys_down(&self, keys: &[VirtualKeyCode]) -> bool {
+ keys.iter().any(|key| self.key_down(*key)) && self.keys_down(keys)

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

This looks like an infinite recursion.

@msiglreith

msiglreith Jun 18, 2017

Contributor

This looks like an infinite recursion.

src/ecs/resources/input.rs
}
/// Checks if all the given mouse buttons are being pressed.
- pub fn mouse_buttons_down(&self, buttons: &[MouseButton]) -> bool {
+ pub fn mouse_buttons_pressed(&self, buttons: &[MouseButton]) -> bool {

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

(Same as above)

@msiglreith

msiglreith Jun 18, 2017

Contributor

(Same as above)

src/ecs/resources/input.rs
- buttons.iter().any(|btn| self.mouse_button_once(*btn)) && self.mouse_buttons_down(buttons)
+ /// Checks if the all the given mouse buttons are down and at least one was pressed this frame.
+ pub fn mouse_buttons_down(&self, buttons: &[MouseButton]) -> bool {
+ buttons.iter().any(|&btn| self.mouse_button_down(btn)) && self.mouse_buttons_down(buttons)

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

(Same as above, recursion)

@msiglreith

msiglreith Jun 18, 2017

Contributor

(Same as above, recursion)

src/ecs/resources/input.rs
+ bad_values.push(*action);
+ }
+ }
+ if bad_values.len() > 0 {

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

nit: !bad_values.is_empty()

@msiglreith

msiglreith Jun 18, 2017

Contributor

nit: !bad_values.is_empty()

src/ecs/resources/input.rs
+ bad_values.push(*action);
+ }
+ }
+ if bad_values.len() > 0 {

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

(Same as above)

@msiglreith

msiglreith Jun 18, 2017

Contributor

(Same as above)

src/ecs/resources/input.rs
+ if let Some(a) = self.axes.get(&id) {
+ let pos = self.button_down(a.pos);
+ let neg = self.button_down(a.neg);
+ if pos == neg {

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

Some( if pos == neg { .. } else if ... ) might be nicer here

@msiglreith

msiglreith Jun 18, 2017

Contributor

Some( if pos == neg { .. } else if ... ) might be nicer here

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 18, 2017

Member
Some(if pos == neg {
                     0.0
                 } else if pos {
                1.0
            } else {
                -1.0
            })

This is the rustfmt version of that. In my opinion it really hurts readability.

@Xaeroxe

Xaeroxe Jun 18, 2017

Member
Some(if pos == neg {
                     0.0
                 } else if pos {
                1.0
            } else {
                -1.0
            })

This is the rustfmt version of that. In my opinion it really hurts readability.

src/ecs/resources/input.rs
+ pub fn action_down(&self, action: i32) -> Option<bool> {
+ self.actions
+ .get(&action)
+ .and_then(|&b| Some(self.button_down(b)))

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

map(|&b| self.button_down(b))) should do the same I think

@msiglreith

msiglreith Jun 18, 2017

Contributor

map(|&b| self.button_down(b))) should do the same I think

src/ecs/resources/input.rs
+ // can't think of a use case for it.
+
+ /// Checks if the given button is currently pressed.
+ pub fn action_pressed(&self, action: i32) -> Option<bool> {

This comment has been minimized.

@msiglreith

msiglreith Jun 18, 2017

Contributor

yay, actions!

@msiglreith

msiglreith Jun 18, 2017

Contributor

yay, actions!

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

Oh man @msiglreith found lots of problematic typos. Thanks a ton @msiglreith I'll get to work on your comments.

Member

Xaeroxe commented Jun 18, 2017

Oh man @msiglreith found lots of problematic typos. Thanks a ton @msiglreith I'll get to work on your comments.

Xaeroxe added some commits Jun 18, 2017

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

Code has been rearranged to have specific InputFrameData structures, this is nice because it more clearly separates where the functionality lives and makes my code less error prone.

Ready for review.

Member

Xaeroxe commented Jun 18, 2017

Code has been rearranged to have specific InputFrameData structures, this is nice because it more clearly separates where the functionality lives and makes my code less error prone.

Ready for review.

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

@msiglreith since all your comments are outdated I'm giving you a ❤️ here, thanks for catching all the typos I made while tired.

Member

Xaeroxe commented Jun 18, 2017

@msiglreith since all your comments are outdated I'm giving you a ❤️ here, thanks for catching all the typos I made while tired.

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

Just added _released functions to detect when an input is released.

Member

Xaeroxe commented Jun 18, 2017

Just added _released functions to detect when an input is released.

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 18, 2017

Member

I skipped having functions like keys_released because the behavior of that would be pretty wonky and hard to predict. It'd only be reasonable to check if any of the given keys were released this frame, which is not how it's opposite keys_down behaves. You can make keys_down work because it relies on action rather than inaction, but keys_released would rely on inaction making it a bit confusing.

Member

Xaeroxe commented Jun 18, 2017

I skipped having functions like keys_released because the behavior of that would be pretty wonky and hard to predict. It'd only be reasonable to check if any of the given keys were released this frame, which is not how it's opposite keys_down behaves. You can make keys_down work because it relies on action rather than inaction, but keys_released would rely on inaction making it a bit confusing.

@ebkalderon

Terrific work all around, @Xaeroxe! Just a few nits to handle before merging.

src/ecs/resources/input.rs
+ }
+ }
+
+ fn advance_frame(&mut self, previous_frame: &mut FrameInputData) {

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Missing a doc comment here.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Missing a doc comment here.

+ }
+
+ // Pressed actions has been omitted as the function is a little difficult to write and I
+ // can't think of a use case for it.

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Given that action_pressed() is present below, this comment seems somewhat misleading.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Given that action_pressed() is present below, this comment seems somewhat misleading.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

Maybe this is a good reason to rename these functions. This comment is actually referring to pressed_actions, with pressed_ in front and an s on actions. It's a different operation from action_pressed. See key_pressed vs pressed_keys for a comparison.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

Maybe this is a good reason to rename these functions. This comment is actually referring to pressed_actions, with pressed_ in front and an s on actions. It's a different operation from action_pressed. See key_pressed vs pressed_keys for a comparison.

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Oh, I see what you mean now. Sorry, the terminology is a bit confusing.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Oh, I see what you mean now. Sorry, the terminology is a bit confusing.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

I just renamed the functions to
pressed_keys()
key_is_pressed()
keys_are_pressed()

I think that helps disambiguate it a bit better.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

I just renamed the functions to
pressed_keys()
key_is_pressed()
keys_are_pressed()

I think that helps disambiguate it a bit better.

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Sounds great! Thank you.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Sounds great! Thank you.

+ let mouse_button_convert = mouse_button_to_button as fn(&MouseButton) -> Button;
+ let key_convert = key_to_button as fn(&VirtualKeyCode) -> Button;
+ let mouse_buttons = self.pressed_mouse_buttons().map(mouse_button_convert);
+ let keys = self.pressed_keys().map(key_convert);

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Perhaps you could use closures here instead of free-standing functions?

@ebkalderon

ebkalderon Jun 19, 2017

Member

Perhaps you could use closures here instead of free-standing functions?

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

I tried, it seems rustc doesn't support casting closures to fn types yet. I spent a few hours trying to get this to work and this was the best I could do. Storing functions in types without using trait objects or generics turns out to be a bit of a sore spot for Rust currently.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

I tried, it seems rustc doesn't support casting closures to fn types yet. I spent a few hours trying to get this to work and this was the best I could do. Storing functions in types without using trait objects or generics turns out to be a bit of a sore spot for Rust currently.

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

I meant passing closures into the map() methods directly, rather than storing them as types.

@ebkalderon

ebkalderon Jun 19, 2017

Member

I meant passing closures into the map() methods directly, rather than storing them as types.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

I sincerely would love to do this, but this is the compiler error I get:

jacob@jacob-laptop ~/amethyst $ cargo check
   Compiling amethyst v0.4.3 (file:///home/jacob/amethyst)
error: non-capturing closure to fn coercion is experimental (see issue #39817)
   --> src/ecs/resources/input.rs:139:36
    |
139 |         let mouse_button_convert = (|&mb| Button::Mouse(mb)) as fn(&MouseButton) -> Button;
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^

error: non-scalar cast: `[closure@src/ecs/resources/input.rs:139:36: 139:61]` as `fn(&glutin::MouseButton) -> ecs::resources::input::Button`
   --> src/ecs/resources/input.rs:139:36
    |
139 |         let mouse_button_convert = (|&mb| Button::Mouse(mb)) as fn(&MouseButton) -> Button;
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: non-capturing closure to fn coercion is experimental (see issue #39817)
   --> src/ecs/resources/input.rs:140:27
    |
140 |         let key_convert = (|&k| Button::Key(k)) as fn(&VirtualKeyCode) -> Button;
    |                           ^^^^^^^^^^^^^^^^^^^^^

error: non-scalar cast: `[closure@src/ecs/resources/input.rs:140:27: 140:48]` as `fn(&glutin::VirtualKeyCode) -> ecs::resources::input::Button`
   --> src/ecs/resources/input.rs:140:27
    |
140 |         let key_convert = (|&k| Button::Key(k)) as fn(&VirtualKeyCode) -> Button;
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 4 previous errors

error: Could not compile `amethyst`.

To learn more, run the command again with --verbose.

These fns have to be stored in the PressedButtons iterator that is passed back so it is necessary to define a type for them. I could just Box the iterator but then we're moving this structure onto the heap unnecessarily.

Basically this whole system ends up being a type system jigsaw puzzle and this was the only way I could see to solve it and maintain performance.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

I sincerely would love to do this, but this is the compiler error I get:

jacob@jacob-laptop ~/amethyst $ cargo check
   Compiling amethyst v0.4.3 (file:///home/jacob/amethyst)
error: non-capturing closure to fn coercion is experimental (see issue #39817)
   --> src/ecs/resources/input.rs:139:36
    |
139 |         let mouse_button_convert = (|&mb| Button::Mouse(mb)) as fn(&MouseButton) -> Button;
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^

error: non-scalar cast: `[closure@src/ecs/resources/input.rs:139:36: 139:61]` as `fn(&glutin::MouseButton) -> ecs::resources::input::Button`
   --> src/ecs/resources/input.rs:139:36
    |
139 |         let mouse_button_convert = (|&mb| Button::Mouse(mb)) as fn(&MouseButton) -> Button;
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: non-capturing closure to fn coercion is experimental (see issue #39817)
   --> src/ecs/resources/input.rs:140:27
    |
140 |         let key_convert = (|&k| Button::Key(k)) as fn(&VirtualKeyCode) -> Button;
    |                           ^^^^^^^^^^^^^^^^^^^^^

error: non-scalar cast: `[closure@src/ecs/resources/input.rs:140:27: 140:48]` as `fn(&glutin::VirtualKeyCode) -> ecs::resources::input::Button`
   --> src/ecs/resources/input.rs:140:27
    |
140 |         let key_convert = (|&k| Button::Key(k)) as fn(&VirtualKeyCode) -> Button;
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 4 previous errors

error: Could not compile `amethyst`.

To learn more, run the command again with --verbose.

These fns have to be stored in the PressedButtons iterator that is passed back so it is necessary to define a type for them. I could just Box the iterator but then we're moving this structure onto the heap unnecessarily.

Basically this whole system ends up being a type system jigsaw puzzle and this was the only way I could see to solve it and maintain performance.

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

I'm sorry, perhaps I'm not being very clear. I meant you could do something like:

let mouse_buttons = self.pressed_mouse_buttons().map(|mb| Button::Mouse(mb));
let keys = self.pressed_keys().map(|k| Button::Key(k));
@ebkalderon

ebkalderon Jun 19, 2017

Member

I'm sorry, perhaps I'm not being very clear. I meant you could do something like:

let mouse_buttons = self.pressed_mouse_buttons().map(|mb| Button::Mouse(mb));
let keys = self.pressed_keys().map(|k| Button::Key(k));

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

Tried that too, with that you get

jacob@jacob-laptop ~/amethyst $ cargo check
   Compiling amethyst v0.4.3 (file:///home/jacob/amethyst)
error[E0308]: mismatched types
   --> src/ecs/resources/input.rs:141:36
    |
141 |         PressedButtons { iterator: mouse_buttons.chain(keys) }
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
    |
    = note: expected type `std::iter::Chain<std::iter::Map<ecs::resources::input::PressedMouseButtons<'_>, fn(&glutin::MouseButton) -> ecs::resources::input::Button>, std::iter::Map<ecs::res
ources::input::PressedKeys<'_>, fn(&glutin::VirtualKeyCode) -> ecs::resources::input::Button>>`
               found type `std::iter::Chain<std::iter::Map<ecs::resources::input::PressedMouseButtons<'_>, [closure@src/ecs/resources/input.rs:139:62: 139:85]>, std::iter::Map<ecs::resources
::input::PressedKeys<'_>, [closure@src/ecs/resources/input.rs:140:44: 140:63]>>`

error: aborting due to previous error


error: Could not compile `amethyst`.

To learn more, run the command again with --verbose.
@Xaeroxe

Xaeroxe Jun 19, 2017

Member

Tried that too, with that you get

jacob@jacob-laptop ~/amethyst $ cargo check
   Compiling amethyst v0.4.3 (file:///home/jacob/amethyst)
error[E0308]: mismatched types
   --> src/ecs/resources/input.rs:141:36
    |
141 |         PressedButtons { iterator: mouse_buttons.chain(keys) }
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
    |
    = note: expected type `std::iter::Chain<std::iter::Map<ecs::resources::input::PressedMouseButtons<'_>, fn(&glutin::MouseButton) -> ecs::resources::input::Button>, std::iter::Map<ecs::res
ources::input::PressedKeys<'_>, fn(&glutin::VirtualKeyCode) -> ecs::resources::input::Button>>`
               found type `std::iter::Chain<std::iter::Map<ecs::resources::input::PressedMouseButtons<'_>, [closure@src/ecs/resources/input.rs:139:62: 139:85]>, std::iter::Map<ecs::resources
::input::PressedKeys<'_>, [closure@src/ecs/resources/input.rs:140:44: 140:63]>>`

error: aborting due to previous error


error: Could not compile `amethyst`.

To learn more, run the command again with --verbose.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

I spent hours trying to jam a closure in there and I couldn't see a way to do it. I'm sorry if I come off flippant, it's just the majority of the time spent on this PR was trying to get this piece of code to compile.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

I spent hours trying to jam a closure in there and I couldn't see a way to do it. I'm sorry if I come off flippant, it's just the majority of the time spent on this PR was trying to get this piece of code to compile.

This comment has been minimized.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

Relevant rustc issue: rust-lang/rust#39817

It seems that we'll be able to correct this code in a very short time, as this has been stabilized in the master branch of rustc.

@Xaeroxe

Xaeroxe Jun 19, 2017

Member

Relevant rustc issue: rust-lang/rust#39817

It seems that we'll be able to correct this code in a very short time, as this has been stabilized in the master branch of rustc.

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

@Xaeroxe No problem at all; I was just curious. 😁 Thank you for all your effort!

@ebkalderon

ebkalderon Jun 19, 2017

Member

@Xaeroxe No problem at all; I was just curious. 😁 Thank you for all your effort!

src/ecs/resources/input.rs
+ pub neg: Button,
+}
+
+#[derive(Default)]

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Missing doc comment here.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Missing doc comment here.

src/ecs/resources/input.rs
+ }
+}
+
+pub struct Axis {

This comment has been minimized.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Missing doc comment here.

@ebkalderon

ebkalderon Jun 19, 2017

Member

Missing doc comment here.

Xaeroxe added some commits Jun 19, 2017

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 19, 2017

Member

@ebkalderon ready for your review again

Member

Xaeroxe commented Jun 19, 2017

@ebkalderon ready for your review again

@Xaeroxe Xaeroxe closed this Jun 19, 2017

@Xaeroxe Xaeroxe reopened this Jun 19, 2017

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 19, 2017

Member

Woops, closing was a misclick, my bad.

Member

Xaeroxe commented Jun 19, 2017

Woops, closing was a misclick, my bad.

@ebkalderon

This comment has been minimized.

Show comment
Hide comment
@ebkalderon

ebkalderon Jun 19, 2017

Member

@Xaeroxe Thank you so much for addressing all my comments. LGTM!

Member

ebkalderon commented Jun 19, 2017

@Xaeroxe Thank you so much for addressing all my comments. LGTM!

@Xaeroxe

This comment has been minimized.

Show comment
Hide comment
@Xaeroxe

Xaeroxe Jun 19, 2017

Member

Excellent! So what's our usual procedure for merging? It's gotten approval from 3 collaborators, so I think it's ready now.

Member

Xaeroxe commented Jun 19, 2017

Excellent! So what's our usual procedure for merging? It's gotten approval from 3 collaborators, so I think it's ready now.

@ebkalderon

This comment has been minimized.

Show comment
Hide comment
@ebkalderon

ebkalderon Jun 19, 2017

Member

Aside from pinging @torkleyy that he should probably rebase #244 to the latest upstream, nothing else. Let's merge this now.

Member

ebkalderon commented Jun 19, 2017

Aside from pinging @torkleyy that he should probably rebase #244 to the latest upstream, nothing else. Let's merge this now.

@ebkalderon ebkalderon merged commit 1cc906d into amethyst:develop Jun 19, 2017

1 of 2 checks passed

continuous-integration/appveyor/pr AppVeyor build failed
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

@nchashch nchashch referenced this pull request Jun 19, 2017

Closed

Release 0.5.0 #241

13 of 13 tasks complete

@Xaeroxe Xaeroxe referenced this pull request Jun 23, 2017

Closed

Input manager #225

0 of 2 tasks complete

@Xaeroxe Xaeroxe deleted the Xaeroxe:input-rebind-patch branch Jun 23, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment