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

Wasm support #759

Merged
merged 45 commits into from
Apr 15, 2020
Merged

Wasm support #759

merged 45 commits into from
Apr 15, 2020

Conversation

elrnv
Copy link
Contributor

@elrnv elrnv commented Mar 26, 2020

This is a continuation of #30 and includes all the changes made there + support for scrolling and keyboard input.

As mentioned in #30, there is an issue with std::time, which was solved with the instant crate, but I believe there are other options available as well.

A demo of the working widgets is available.

The limitations in this PR AFAICT are:

  • Loading images
  • Clipboard implementation
  • File dialog handling
  • Menu (this may require designing a custom menu widget)

This PR also depends on a pull request in piet #153, which makes returning the piet context easier.

Edit: Fixes #128

@luleyleo
Copy link
Collaborator

I've been playing around with the demo (in Firefox and Chromium) and everything seems to work, one exception being zoom, which seems to break things quite a bit.

When loading example with zoom 100%:

  • Zooming in makes content smaller
  • Zooming out makes content larger
  • Zooming in any way breaks cursor position

When loading example with zoom > 100%:

  • Example displays at normal (100%) size

When loading example with zoom < 100%:

  • White bar at the right (in the backround)
  • Druid can draw above it
  • Leaves artifacts behind when druid stops drawing over the white bar (flex example)

In both cases != 100%

  • Zooming in any direction / resizing the window makes it jump to the chosen zoom level (for example when loading at 200% an pressing - sets zoom to 170% and then it looks like when loaded at 100% and then zoomed to 170%

@elrnv
Copy link
Contributor Author

elrnv commented Mar 27, 2020

Thanks @Finnerale! I believe I have resolve the issues you found with zooming. This relies on #158 being merged as well.

It seems the zooming issue was in part because the context wasn't scaled properly during the resize callback.

@elrnv
Copy link
Contributor Author

elrnv commented Mar 27, 2020

I organized a small repo for building and hosting these examples on the web at https://github.com/elrnv/druid-wasm-examples/, hopefully it can serve as a reference on how one would go about building and deploying these examples to the web. It certainly took me some time to figure it out.

@ijsnow
Copy link

ijsnow commented Mar 28, 2020

Woah! Amazing timing! I just discovered druid and was hoping for wasm support and here it is.

It appears another limitation of the current state is that it doesn't capture the browser's native functionality of navigating back when you press "backspace" when a text box is focused

@elrnv
Copy link
Contributor Author

elrnv commented Mar 28, 2020

Oh thanks for finding that @ijsnow, in my setup, the browser doesn't respond to backspace so I never noticed this. I was able to reproduce this on another machine, and indeed it makes the text widgets basically unusable. I think this would be an unacceptable limitation, so I patched it by disabling the default backspace functionality altogether.

Perhaps this is too conservative and the default backspace behaviour should be enabled when the text widget is not focused, but for this PR I think this is acceptable since most likely the kinds of web applications that would require a GUI like druid would opt for a custom backspace behaviour anyway.

The druid-wasm-examples should be updated with the fix now. It would be great if you can verify if this issue is resolved there on your end :)

@elrnv
Copy link
Contributor Author

elrnv commented Mar 30, 2020

@cmyr, @raphlinus: Is there anything else that needs to be done to get this through, or is it just not a priority right now?

@cmyr
Copy link
Member

cmyr commented Mar 30, 2020

No, I'd like to get this merged while it's warm, just haven't built up the courage to give it a closer look. will do that soon!

@cmyr cmyr added the S-needs-review waits for review label Apr 1, 2020
Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

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

Okay I have some notes but this looks like a pretty good place to start, I'm happy to merge this as a checkpoint soon.

druid/src/contexts.rs Outdated Show resolved Hide resolved
druid-shell/Cargo.toml Outdated Show resolved Hide resolved
druid-shell/src/platform/web/window.rs Outdated Show resolved Hide resolved
druid-shell/src/platform/web/window.rs Outdated Show resolved Hide resolved
@cmyr cmyr added S-waiting-on-author waits for changes from the submitter and removed S-needs-review waits for review labels Apr 3, 2020
@elrnv
Copy link
Contributor Author

elrnv commented Apr 4, 2020

All review comments should be addressed in 3813e3c. Thanks for the feedback!
I should mention that this PR depends on linebender/piet#153 and a few other piet PRs that have already been merged but aren't in the release yet.

@futurepaul futurepaul mentioned this pull request Apr 4, 2020
@elrnv
Copy link
Contributor Author

elrnv commented Apr 8, 2020

The last PR linebender/piet#153 was merged, so this PR is just blocked on having another piet release.

@cmyr
Copy link
Member

cmyr commented Apr 12, 2020

@elrnv the piet changes have made it to druid.

elrnv added 10 commits April 12, 2020 10:25
A basic wasm backend is added as a continuation of linebender#30.
Since std::time isn't available on wasm, an additional dependency is
added to abstract std::time::Instant to work with wasm.
This commit ensures that all examples run with minimal changes.
More specifically we don't need to remove the .use_simple_logger() call
on all Apps.
The resize callback should be registered to the window, since resizing
the window doesn't call the resize callback for canvases even if those
do get resized.
The scroll example now works as expected on hi and low dpi screens.
This makes passing it easier to pass owned strings when creating a new
KeyEvent, which is necessary when interfacing with WASM.
This should be the default behavior to make text widgets usable.
At a later iteration it may be better to only disable the browser from
going back when a widget is selected that is expecting keyboard input
where backspace is intended for something else.

I expect this functionality would require some way of accessing the
web_sys event from within a druid callback.
The newly introduced lifetime parameter must be specified exmplicitly.
@elrnv
Copy link
Contributor Author

elrnv commented Apr 12, 2020

@cmyr Thanks! The PR is ready now.

Copy link
Collaborator

@luleyleo luleyleo left a comment

Choose a reason for hiding this comment

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

That's a lot of work here, thanks 🎉 !
I've left some comments and requests tho.

The biggest open issues seem to me testing and clippy (which behaves different for wasm?)
But those can be handled in future PRs.

druid-shell/src/platform/web/application.rs Outdated Show resolved Hide resolved
druid-shell/src/platform/web/dialog.rs Outdated Show resolved Hide resolved
druid-shell/src/platform/web/window.rs Outdated Show resolved Hide resolved
Comment on lines +449 to +455
state
.window
.set_timeout_with_callback_and_timeout_and_arguments_0(
Closure::once_into_js(f).as_ref().unchecked_ref(),
interval,
)
.expect("Failed to call setTimeout with a callback");
Copy link
Collaborator

Choose a reason for hiding this comment

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

I haven't worked with wasm yet, but in other places with Closure there is allways a call to forget

let closure = Closure::wrap(Box::new(f) as Box<dyn FnMut(_)>);
window_state
    .window
    .add_event_listener_with_callback(event_type, closure.as_ref().unchecked_ref())
    .unwrap();
closure.forget();

Not sure if this would need one as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From what I understand this is true for Closures created with wrap or once, but once_into_js is special in that it does the forgetting for us. Quoting the wasm_bindgen docs for once_into_js:

Unlike Closure::once, this does not return a Closure that can be dropped before the function is invoked to deallocate the closure.

druid-shell/src/platform/web/window.rs Outdated Show resolved Hide resolved
druid/examples/wasm/.gitignore Outdated Show resolved Hide resolved
druid/examples/wasm/Cargo.toml Show resolved Hide resolved
Comment on lines 1 to 2
//! This module is intentionally left blank.
//! The contents are automatically generated in the "build.rs" script.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would prefer if this file would not exist at all before building the wasm examples.
Because this file was added to git, the .gitignore entry for it will have no effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I readjusted this mechanism to avoid any changes to any committed files in e072555. Would this work?

Comment on lines +128 to 130
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a benefit for making the tests work on wasm?
On a quick glance this seems to test nothing platform specific @cmyr?
If it does tho, there should be an issue opened for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure about this one. I left it out for the time being because it requires changes in piet to get this to compile, (I submitted a PR to add the missing types to piet in linebender/piet#167) and I'm not really familiar with the testing code. I suspect it can be used for testing although it needs to be run in a special way for wasm and not through cargo test, but I'm not sure. Is this appropriate for a separate PR? For reference there is some code testing the wasm backend in piet.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sure, this can be done in a later PR, if it makes sens to do so.
Which is what I hoped @cmyr would enlighten me about.

Copy link
Member

Choose a reason for hiding this comment

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

yea, happy for this to be followup.

Comment on lines +15 to +16
#![allow(clippy::unreadable_literal)]

Copy link
Collaborator

Choose a reason for hiding this comment

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

Wow, that's odd. Does not happen without --target wasm32-unknown-unknown 😒
I could not find out why clippy does that tho

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could be wrong but my impression was that the examples were actually not checked by clippy before, but since the druid-wasm-examples is now in the workspace, they all get checked with cargo clippy --all -- -D warnings with or without --target wasm32-unknown-unknown. I remember having to make some other tweaks to the examples to satisfy clippy.

elrnv and others added 7 commits April 13, 2020 11:10
This commit exposes the error of the missing implementation for file dialogs in the web backend using log::warn. This is a temporary solution until the `open_file_sync` and `save_as_sync` functions propagate the error downstream using Result instead of Option.

Co-Authored-By: Leopold Luley <[email protected]>
The types in that module have since made their way into druid itself.
This commit attempts a different approach at including the automatically
generated examples from the parent directory.
This method satisfies both rustfmt and cargo test as before, but it also
doesn't modify any files in the source tree, keeping the diff clean
after a build.
@elrnv
Copy link
Contributor Author

elrnv commented Apr 13, 2020

Thanks for the feedback! I tried to address all of the comments as best as I can. Passing the baton back now :)

Copy link
Collaborator

@luleyleo luleyleo left a comment

Choose a reason for hiding this comment

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

Code wise I'm happy now 😄

I installed wasm-pack and things worked out well!
Playing with the examples I've discovered two issues:

  • The switch example has an unfortunate name for JS

  • The identity example color cycling is limited to red
    probably instant::Instant being less accurate
    As this does not really matter for the example I think this can be ignored

I personally only care about the first point

This commit fixes the generated html file for the switch example to load
"switch_demo" instead of "switch" for the switch example. The word
switch conflicts with JavaScript's switch statement, which is the reason
for this awkwardness.
@elrnv
Copy link
Contributor Author

elrnv commented Apr 14, 2020

Regarding the first bullet, for the time being the switch example is renamed to switch_demo when building the WASM interface (the actual issue should be fixed in 3a528fa). Perhaps in another PR we could rename the example itself to switch_demo or something like that, which will clean up a bit of code from the WASM build.rs and lib.rs files.

I did a little digging about the second bullet to find the [MDN article on performance.now()]
(https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) which mentions that browsers reduce the precision of time for privacy reasons.

Copy link
Collaborator

@luleyleo luleyleo left a comment

Choose a reason for hiding this comment

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

I've nothing more to add to this 👍
Would be happy to merge it.

@luleyleo luleyleo added S-needs-review waits for review and removed S-waiting-on-author waits for changes from the submitter labels Apr 14, 2020
Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

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

I've done a scan and everything seems reasonable; I'm happy to at least get this in as a checkpoint!

@luleyleo luleyleo merged commit 37b9646 into linebender:master Apr 15, 2020
@elrnv elrnv deleted the wasm-support branch April 15, 2020 17:37
@xStrom xStrom removed the S-needs-review waits for review label May 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

wasm support
5 participants