Facilitate using step() while processing other events
I'm trying to use this library in a way where I have multiple windows that are stepped by a thread (which happens to be the main thread), and then a background thread uses MPSC to send commands to the window stepping thread to make things happen. The problem is that webview as it is right now blocks on step() until events need to be processed, thus leading to my MPSC events not processing correctly. My window stepping thread loops roughly like this (simplified to get the point across):
loop{
while let Ok(msg) = mpsc_recv.recv(){
match msg{
// ...
}
}
for window in &windows{
let result = window.step(); //blocks here
if result.is_none(){
// remove from windows list
}
}
}
When step() blocks on some window, mpsc_recv's events do not get processed, which is problematic. I could create a complicated severally-threaded system to process those events via another background thread that uses window handles, but this is very much non-ideal due to the complexity and it's not even a complete solution as window creation is still blocked by step() (one of the MPSC command types creates a new window).
I see a few solutions to this problem, ordered in descending order of what I think is best:
- Add some way to wake up the windows from another thread — it looks like step() when blocking on any window will wake up if any other window receives an event, so it must be some sort of global event bus. As such, a web_view::notify_event_bus() that can be called from any thread would be a good API.
- Add a timeout to step(), such that it will not block for more than, for example, a 60th of a second, or, even better, integrate with the underlying platform API to get the display frequency of the fastest display and do not block for longer than that
- Allow an option for step() to be non-blocking. This should be a very easy change. See #263 and #218, but it will introduce some amount of busy waiting which is another problem. If this were implemented, I'd have to do my own sleeps between event processing, but still process at a high enough frequency (e.g. at the display's frequency).
Quite late since it has been a couple of years since this issue was opened, but I encountered this same roadblock yesterday.
You can fix easily using webview.run() on a thread and then on a different thread using the .dispatch function to send the events.
This is what I'm doing:
let mut view = web_view::builder()
.content(Content::Url("http://127.0.0.1:62000/"))
.resizable(true)
.size(1600, 900)
.debug(true)
.user_data(())
.invoke_handler(|_webview, _arg| Ok(()))
.build()
.unwrap();
// Create a task that will run in parallel, in another tokio thread
let _t = tokio::spawn({
let ui_tx = ui_tx;
async move {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
ui_tx.send(serde_json::json!({ "example": "data" })).await;
}
}
});
// grab the view handle
let view_handle = view.handle();
// create another task that will receive the ui messages
let _event_sender = tokio::spawn(async move {
loop {
if let Some(message) = ui_rx.recv().await {
// Relay the messages to the UI
view_handle
.dispatch(move |view| {
view.eval(&format!(
"
{{
const event = new Event(\"message\");
event.data = {};
window.dispatchEvent(event);
}}
",
serde_json::to_string(&message).unwrap()
))
})
.unwrap();
}
}
});
view.run().unwrap();