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

New keyboard API for the web #1888

Closed
wants to merge 13 commits into from

Conversation

maroider
Copy link
Member

@maroider maroider commented Mar 18, 2021

  • 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
  • Updated feature matrix, if new features were added or implemented

This implements #753 for WASM.

TODOs

  • Decide if a stdweb backend is worthwhile, since it's deprecated.
    • Implement a stdweb backend if need be.
  • A bunch of TODO comments in the code.
  • Debug why my example program sometimes freezes with certain input sequences.

@maroider maroider closed this Mar 18, 2021
@maroider maroider reopened this Mar 18, 2021
src/keyboard.rs Outdated Show resolved Hide resolved
src/keyboard.rs Outdated Show resolved Hide resolved
@ArturKovacs
Copy link
Contributor

For the record: I'll test and properly review this once the merge conflicts are resolved.

@maroider maroider added DS - x11 C - in progress Implementation is proceeding smoothly DS - web and removed DS - x11 labels May 7, 2021
@maroider maroider mentioned this pull request May 7, 2021
20 tasks
@maroider maroider added this to the Keyboard Events Overhaul milestone May 7, 2021
@maroider maroider force-pushed the new-keyboard-web branch 3 times, most recently from 9e9b317 to d7c9b81 Compare May 14, 2021 16:13
@maroider
Copy link
Member Author

Welp, I messed up the merge more times than I'd like to admit, but the merge conflicts should be resolved now.

@maroider maroider force-pushed the new-keyboard-web branch 2 times, most recently from 3391b55 to 16bb871 Compare May 14, 2021 16:29
Copy link
Contributor

@ArturKovacs ArturKovacs left a comment

Choose a reason for hiding this comment

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

Coming along nicely

src/keyboard.rs Outdated Show resolved Hide resolved
src/platform_impl/web/event_loop/window_target.rs Outdated Show resolved Hide resolved
src/platform_impl/web/mod.rs Outdated Show resolved Hide resolved
src/platform_impl/web/keyboard.rs Outdated Show resolved Hide resolved
src/platform_impl/web/event_loop/window_target.rs Outdated Show resolved Hide resolved
src/platform_impl/web/web_sys/canvas.rs Outdated Show resolved Hide resolved
Comment on lines 77 to 106
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
}
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Member Author

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?

Copy link
Contributor

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.

src/platform_impl/web/web_sys/event.rs Outdated Show resolved Hide resolved
src/platform_impl/web/web_sys/event.rs Outdated Show resolved Hide resolved
src/platform_impl/web/web_sys/event.rs Show resolved Hide resolved
@ArturKovacs
Copy link
Contributor

Furthermore, Space, Tab, and Enter emit None for the text field; they should emit a string. (I believe Enter should be "\r")

@maroider
Copy link
Member Author

Furthermore, Space, Tab, and Enter emit None for the text field; they should emit a string. (I believe Enter should be "\r")

This should be fixed now.

What remains now is a single TODO comment as well as debugging the strange freezing.

Comment on lines +79 to +83
match &key {
Key::Character(text) => Some(*text),
Key::Tab => Some("\t"),
Key::Enter => Some("\r"),
Key::Space => Some(" "),
Copy link
Contributor

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.

@Woyten
Copy link

Woyten commented Dec 21, 2021

@maroider Is there any update on this topic? I would like to see #2092 fixed. I could do the fixing myself but I am not sure whether my fix would be accepted and/or is in conflict with this PR.

"F33" => Key::F33,
"F34" => Key::F34,
"F35" => Key::F35,
string @ _ => Key::Character(string),
Copy link
Contributor

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.

Comment on lines +89 to +106
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
}
Copy link
Contributor

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.

Comment on lines +18 to +35
#[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))
}
}
Copy link
Contributor

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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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());
Copy link
Contributor

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.

Copy link
Contributor

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.

Comment on lines +134 to +139
// 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()
// }
Copy link
Contributor

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.

@erwanvivien
Copy link

Is there any update on this and how can we contribute if we need to ?

@maroider maroider mentioned this pull request Jan 31, 2023
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C - in progress Implementation is proceeding smoothly DS - web
Development

Successfully merging this pull request may close these issues.

5 participants