-
Notifications
You must be signed in to change notification settings - Fork 914
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
New keyboard API for the web #1888
Conversation
For the record: I'll test and properly review this once the merge conflicts are resolved. |
9e9b317
to
d7c9b81
Compare
Welp, I messed up the merge more times than I'd like to admit, but the merge conflicts should be resolved now. |
3391b55
to
16bb871
Compare
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.
Coming along nicely
pub fn key_text(event: &KeyboardEvent) -> Option<&'static str> { | ||
let key = event.key(); | ||
if let Key::Character(text) = Key::from_key_attribute_value(&key) { | ||
// TODO: Fix unbounded leak | ||
Some(Box::leak(String::from(text).into_boxed_str())) | ||
} else { | ||
None | ||
} |
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.
The way I handled the leaked string, was by creating a global (lazy static) Mutex<HashSet<&'static str>>
. I think the same approach is suitable here as well.
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 copied over what I used in the Linux PR, which is about what you described. I'm a bit uncertain of how mutexes work on WASM, but it seemed to run fine when I tested it.
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.
@ryanisaacg, how do mutexes work on WASM?
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.
WebAssembly has instructions to block until a value in shared memory changes, which I believe is what mutexes use. Issue is, I'm pretty sure it causes an error if the main thread blocks, so we probably shouldn't use them here.
Furthermore, Space, Tab, and Enter emit |
This should be fixed now. What remains now is a single TODO comment as well as debugging the strange freezing. |
match &key { | ||
Key::Character(text) => Some(*text), | ||
Key::Tab => Some("\t"), | ||
Key::Enter => Some("\r"), | ||
Key::Space => Some(" "), |
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.
Note that the Key
has the to_text
method which does pretty much this.
c75b536
to
2adcc7e
Compare
This is to appease `serde` for the time being.
As a direct result of this, `NativeKeyCode`, `Key`, `KeyCode` and `RawKeyEvent` no longer implement `Copy`.
2adcc7e
to
2c7ba77
Compare
1ef0b49
to
54bb969
Compare
"F33" => Key::F33, | ||
"F34" => Key::F34, | ||
"F35" => Key::F35, | ||
string @ _ => Key::Character(string), |
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 don't think the @ _
is needed here.
fn cached_string<S: AsRef<str>>(string: S) -> &'static str { | ||
use std::collections::HashSet; | ||
use std::sync::Mutex; | ||
|
||
lazy_static::lazy_static! { | ||
static ref STRING_CACHE: Mutex<HashSet<&'static str>> = Mutex::new(HashSet::new()); | ||
}; | ||
|
||
let string = string.as_ref(); | ||
let mut cache = STRING_CACHE.lock().unwrap(); | ||
if let Some(string) = cache.get(string) { | ||
*string | ||
} else { | ||
// borrowck couldn't quite figure out this one on its own | ||
let string: &'static str = Box::leak(String::from(string).into_boxed_str()); | ||
cache.insert(string); | ||
string | ||
} |
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.
What's the reason why we need an &'static str
in the first place? It seems reasonable to me for KeyEvent
and Key
to contain owned strings instead and avoid this complexity.
#[derive(Default)] | ||
struct ModifiersShared(Rc<Cell<ModifiersState>>); | ||
|
||
impl ModifiersShared { | ||
fn set(&self, new: ModifiersState) { | ||
self.0.set(new) | ||
} | ||
|
||
fn get(&self) -> ModifiersState { | ||
self.0.get() | ||
} | ||
} | ||
|
||
impl Clone for ModifiersShared { | ||
fn clone(&self) -> Self { | ||
Self(Rc::clone(&self.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.
I think this could be replaced with type ModifiersShared = Rc<Cell<ModifiersState>>
instead of having to declare a newtype.
event.key().chars().next().unwrap() | ||
// TODO: What should be done about `KeyboardEvent.isComposing`? | ||
|
||
pub fn keyboard_modifiers(key: &Key<'_>) -> ModifiersState { |
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.
pub fn keyboard_modifiers(key: &Key<'_>) -> ModifiersState { | |
/// Returns the keyboard modifiers that changed with this keypress. | |
/// | |
/// For a pressed key, this is the keyboard modifiers that are newly pressed, | |
/// and for a released key, this is the keyboard modifiers that are newly released. | |
pub fn keyboard_modifiers(key: &Key<'_>) -> ModifiersState { |
Reading the code that was using this function, I assumed that this still had the old behaviour of returning the current state of the modifiers rather than the change to the modifiers. (It might also be a good idea to rename it to changed_modifiers
or something.)
As a side-note, why was it changed to this in the first place? The old way of getting the current state seems easier to work with IMO.
use std::sync::Mutex; | ||
|
||
lazy_static::lazy_static! { | ||
static ref STRING_CACHE: Mutex<HashSet<&'static str>> = Mutex::new(HashSet::new()); |
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.
The main thread isn't allowed to block on the web, so a Mutex
probably shouldn't be used here. A thread_local!
should work.
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.
To be fair, I don't think the event loop can be run outside of the main thread so this probably won't be a problem in practice, but that's even more of a reason to use a thread local.
// pub fn codepoint(event: &KeyboardEvent) -> char { | ||
// // `event.key()` always returns a non-empty `String`. Therefore, this should | ||
// // never panic. | ||
// // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key | ||
// event.key().chars().next().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.
This should probably be removed.
Is there any update on this and how can we contribute if we need to ? |
cargo fmt
has been run on this branchcargo doc
builds successfullyCHANGELOG.md
if knowledge of this change could be valuable to usersCreated or updated an example program if it would help users understand this functionalityThis implements #753 for WASM.
TODOs
stdweb
backend is worthwhile, since it's deprecated.Implement astdweb
backend if need be.TODO
comments in the code.