-
-
Notifications
You must be signed in to change notification settings - Fork 34
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
Add support for relative and absolute input remapping #57
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this!
I have quite a few comments about the implementation; I'm finding it difficult to follow the changes and I think that being a bit more deliberate about scoping the changes will help with that!
|
||
impl PartialEq for KeyCodeWrapper { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.code == other.code && self.scale.is_negative() == other.scale.is_negative() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add comments both here and on the scale
field above about why only the magnitude is considered for equality and hashing, as it is not obvious from the code!
I would prefer to use the compiler-provided derive for PartialEq, Eq, Hash
because it can be tricky to keep things straight, and I'm not sure why this is being special cased.
match s.rmatch_indices(&['+', '-']).next() { | ||
None => { | ||
name = &s; | ||
scale = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems surprising to me that scale could validly be 0; I would have assumed that it would always be >= 1.
let _scale; | ||
(name, _scale) = s.split_at(m.0); | ||
if _scale.len() > 1 { | ||
scale = _scale.parse::<i32>().unwrap(); | ||
} else if _scale == "-" { | ||
scale = -1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stylistically speaking, this extra _scale
is "bad" for two reasons:
- There is a rust convention for marking a variable binding as unused by prefixing it with
_
, but this variable is not unused. - It feels a bit awkward to semi-shadow the scale value in the outer scope.
let _scale; | |
(name, _scale) = s.split_at(m.0); | |
if _scale.len() > 1 { | |
scale = _scale.parse::<i32>().unwrap(); | |
} else if _scale == "-" { | |
scale = -1; | |
} | |
let (name, scale_str) = s.split_at(m.0); | |
if scale_str.len() > 1 { | |
scale = scale_str.parse::<i32>().unwrap(); | |
} else if scale_str == "-" { | |
scale = -1; | |
} |
let _scale; | ||
(name, _scale) = s.split_at(m.0); | ||
if _scale.len() > 1 { | ||
scale = _scale.parse::<i32>().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, bare unwrap()
is to be avoided. Alternatives:
- If the condition is guaranteed impossible, or is guaranteed to require immediate program termination, then substitute it with
.expect("reason why it can never happen")
so that there is an explanation of the assumption that can help to debug it when it actually does happen. This is not appropriate in this context, because we're parsing user-provided input. - In a function, like this, that returns
Result
, then it should instead be changed to propagate the error value in a meaningful way. Often you can use the?
operator for this, but withanyhow
we can also provide some more useful context:
scale = _scale.parse::<i32>().unwrap(); | |
scale = _scale.parse::<i32>().context("failed to parse expected scale factor as an integer")?; |
if prefix == "KEY" && scale == 0 { | ||
scale = 1; | ||
} | ||
match EventType::from_str(&*("EV_".to_string() + prefix)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit easier to grok:
match EventType::from_str(&*("EV_".to_string() + prefix)) { | |
match EventType::from_str(&format("EV_{prefix}")) { |
} | ||
}, | ||
}; | ||
let mut prefix = name.split_once("_").unwrap().0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please refactor to avoid using unwrap
.
}, | ||
}; | ||
let mut prefix = name.split_once("_").unwrap().0; | ||
if prefix == "BTN" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add comments about what is happening here and why
let mut a_ismod = false; | ||
let mut b_ismod = false; | ||
if let EventCode::EV_KEY(k) = a { | ||
a_ismod = is_modifier(&k); | ||
} | ||
if let EventCode::EV_KEY(k) = b { | ||
b_ismod = is_modifier(&k); | ||
} | ||
if a_ismod && b_ismod { | ||
if b_ismod { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this feels harder to read than it was before. Please revert is_modifier
back to its former state (accepting &KeyCode
) to resolve this.
Mapping relative inputs to relative inputs is also possible, you can include | ||
a scaling factor to both the input value and output. The input value is divided | ||
by the input scaling factor, and the output is multiplied by the output scaling | ||
factor. Specify a negative scale factor on the output to invert the input. If you | ||
provide an input scale, you need to remap both relative directions separately: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is quite dense and is hard to immediately grok and relate to the examples below.
Please intersperse this explanation with the examples and explain precisely what happens for each of the examples, as it isn't immediately clear what the +4
on input does in relation to the +2
on output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I think the scale factor looks like addition/subtraction, which makes it a bit confusing, as does the context dependent multiply-or-divide.
How about using something like +WHEEL/4
to indicate positive relative wheel, divided by 4, or -WHEEL*2
to indicate negative relative wheel multiplied by 2?
I think switching the scale from i32
to an f32
is conceptually good for this, as the parser can more directly track the intent that was written out in the config file. I think the wrinkle would be that f32
doesn't support Eq
(only PartialEq
). That could be resolved by using NotNan.
With that approach, the default scale would be 1.0
and the other scaling logic is simply the product of both input and output scale factors.
[[remap]] | ||
input = ["REL_HWHEEL_HI_RES+4"] | ||
output = ["REL_Y+2"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having a hard time grokking what this PR is doing overall, by which I mean: I get the gist of what the higher level goal is, but when it comes to the implementation, the details are not at all clear to me.
I think it is due in part to overloading what was a simple keycode type with something that is conceptually richer, but which in the current implementation feels a little bit like it was "finagled in".
What I think might help is to reduce the scope of the changes around the KeyCodeWrapper
and to make it explicitly handle the situations that are necessary.
For example, rather than a struct it could be an enum, which would allow tracking the scale only for the relative case:
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
enum EventWrapper {
Key(KeyCode),
Relative {
code: KeyCode,
scale: NotNan<f32>,
},
Absolute(KeyCode),
}
This sort of change should make it a bit clearer in the consuming code what's going on.
You are right, I had to do some finagling to get my use case to fit this code base. |
Ah, I can understand moving on to something else, but just FYI, I spent a significant amount of time reading through this PR and working up that review, and also for your other PRs. I feel like I just wasted a good chunk of my productive time this weekend. |
This is my first foray into Rust so please forgive any egregious convention violations. I am happy to fix any.
I have added the ability to remap relative and absolute inputs. This is primarily useful to remap the mouse wheel of my mouse.
This PR should resolve #14