Skip to content

New keyboard API for the web#1888

Closed
maroider wants to merge 13 commits intorust-windowing:new-keyboardfrom
maroider:new-keyboard-web
Closed

New keyboard API for the web#1888
maroider wants to merge 13 commits intorust-windowing:new-keyboardfrom
maroider:new-keyboard-web

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
@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 Affects the X11 backend, or generally free Unix platforms C - in progress Implementation is proceeding smoothly DS - web Affects the Web backend (WebAssembly/WASM) and removed DS - x11 Affects the X11 backend, or generally free Unix platforms 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

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.

@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 Affects the Web backend (WebAssembly/WASM)

Development

Successfully merging this pull request may close these issues.

5 participants