Przeglądaj źródła

fix: use futuresunordered instead of bespoke waker system

Jonathan Kelley 2 lat temu
rodzic
commit
bd720e2268

+ 41 - 0
examples/clock.rs

@@ -0,0 +1,41 @@
+//! Example: README.md showcase
+//!
+//! The example from the README.md.
+
+use dioxus::prelude::*;
+use futures_util::Future;
+
+#[tokio::main]
+async fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    loop {
+        dom.wait_for_work().await;
+    }
+
+    // dioxus_desktop::launch(app).await;
+}
+
+fn app(cx: Scope) -> Element {
+    let mut count = use_ref(cx, || 0);
+
+    let mut ct = count.to_owned();
+    use_coroutine(cx, |_: UnboundedReceiver<()>| async move {
+        loop {
+            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
+
+            *ct.write() += 1;
+
+            let current = *ct.read();
+
+            println!("current: {}", current);
+        }
+    });
+
+    let mut count = count.read();
+
+    cx.render(rsx! {
+        div { "High-Five counter: {count}" }
+    })
+}

+ 3 - 2
examples/todomvc.rs

@@ -3,8 +3,9 @@
 use dioxus::prelude::*;
 use dioxus_elements::input_data::keyboard_types::Key;
 
-fn main() {
-    dioxus_desktop::launch(app);
+#[tokio::main]
+async fn main() {
+    dioxus_desktop::launch(app).await;
 }
 
 #[derive(PartialEq, Eq)]

+ 1 - 1
packages/core/Cargo.toml

@@ -23,7 +23,7 @@ rustc-hash = "1.1.0"
 # Used in diffing
 longest-increasing-subsequence = "0.1.0"
 
-futures-util = { version = "0.3", default-features = false }
+futures-util = { version = "0.3", default-features = false, features = ["alloc"]}
 
 slab = "0.4"
 

+ 4 - 5
packages/core/src/scheduler/mod.rs

@@ -1,14 +1,13 @@
 use crate::ScopeId;
+use futures_util::stream::FuturesUnordered;
 use slab::Slab;
 
 mod suspense;
 mod task;
 mod wait;
-mod waker;
 
 pub use suspense::*;
 pub use task::*;
-pub use waker::ArcWake;
 
 /// The type of message that can be sent to the scheduler.
 ///
@@ -31,17 +30,17 @@ pub(crate) struct Scheduler {
     pub sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
 
     /// Tasks created with cx.spawn
-    pub tasks: RefCell<Slab<Arc<LocalTask>>>,
+    pub tasks: RefCell<FuturesUnordered<LocalTask>>,
 
     /// Async components
-    pub leaves: RefCell<Slab<Arc<SuspenseLeaf>>>,
+    pub leaves: RefCell<Slab<SuspenseLeaf>>,
 }
 
 impl Scheduler {
     pub fn new(sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>) -> Rc<Self> {
         Rc::new(Scheduler {
             sender,
-            tasks: RefCell::new(Slab::new()),
+            tasks: RefCell::new(FuturesUnordered::new()),
             leaves: RefCell::new(Slab::new()),
         })
     }

+ 10 - 3
packages/core/src/scheduler/suspense.rs

@@ -1,4 +1,6 @@
-use super::{waker::ArcWake, SchedulerMsg};
+use futures_util::task::ArcWake;
+
+use super::SchedulerMsg;
 use crate::ElementId;
 use crate::{innerlude::Mutations, Element, ScopeId};
 use std::future::Future;
@@ -40,11 +42,16 @@ pub(crate) struct SuspenseLeaf {
     pub(crate) tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
     pub(crate) notified: Cell<bool>,
     pub(crate) task: *mut dyn Future<Output = Element<'static>>,
+    pub(crate) waker: Arc<SuspenseHandle>,
+}
+
+pub struct SuspenseHandle {
+    pub(crate) id: SuspenseId,
+    pub(crate) tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
 }
 
-impl ArcWake for SuspenseLeaf {
+impl ArcWake for SuspenseHandle {
     fn wake_by_ref(arc_self: &Arc<Self>) {
-        arc_self.notified.set(true);
         _ = arc_self
             .tx
             .unbounded_send(SchedulerMsg::SuspenseNotified(arc_self.id));

+ 40 - 10
packages/core/src/scheduler/task.rs

@@ -1,9 +1,13 @@
-use super::{waker::ArcWake, Scheduler, SchedulerMsg};
+use futures_util::task::ArcWake;
+use futures_util::FutureExt;
+
+use super::{Scheduler, SchedulerMsg};
 use crate::ScopeId;
 use std::cell::RefCell;
 use std::future::Future;
 use std::pin::Pin;
 use std::sync::Arc;
+use std::task::Waker;
 
 /// A task's unique identifier.
 ///
@@ -19,6 +23,17 @@ pub(crate) struct LocalTask {
     pub(super) task: RefCell<Pin<Box<dyn Future<Output = ()> + 'static>>>,
     id: TaskId,
     tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
+    pub waker: Waker,
+}
+impl Future for LocalTask {
+    type Output = ();
+
+    fn poll(
+        self: Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Self::Output> {
+        self.task.borrow_mut().poll_unpin(cx)
+    }
 }
 
 impl Scheduler {
@@ -32,16 +47,23 @@ impl Scheduler {
     /// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which
     /// will only occur when the VirtuaalDom itself has been dropped.
     pub fn spawn(&self, scope: ScopeId, task: impl Future<Output = ()> + 'static) -> TaskId {
-        let mut tasks = self.tasks.borrow_mut();
-        let entry = tasks.vacant_entry();
-        let task_id = TaskId(entry.key());
+        // let entry = tasks.vacant_entry();
+        let task_id = TaskId(0);
 
-        entry.insert(Arc::new(LocalTask {
+        let task = LocalTask {
             id: task_id,
             tx: self.sender.clone(),
             task: RefCell::new(Box::pin(task)),
             scope,
-        }));
+            waker: futures_util::task::waker(Arc::new(LocalTaskHandle {
+                id: task_id,
+                tx: self.sender.clone(),
+            })),
+        };
+
+        self.tasks.borrow_mut().push(task);
+
+        // println!("Spawning task: {:?}", task_id);
 
         self.sender
             .unbounded_send(SchedulerMsg::TaskNotified(task_id))
@@ -54,14 +76,22 @@ impl Scheduler {
     ///
     /// This does nto abort the task, so you'll want to wrap it in an aborthandle if that's important to you
     pub fn remove(&self, id: TaskId) {
-        self.tasks.borrow_mut().remove(id.0);
+        // self.tasks.borrow_mut().remove(id.0);
     }
 }
 
-impl ArcWake for LocalTask {
+pub struct LocalTaskHandle {
+    id: TaskId,
+    tx: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
+}
+
+impl ArcWake for LocalTaskHandle {
     fn wake_by_ref(arc_self: &Arc<Self>) {
-        _ = arc_self
+        println!("waking task: {:?} {:p}", arc_self.id, arc_self);
+
+        arc_self
             .tx
-            .unbounded_send(SchedulerMsg::TaskNotified(arc_self.id));
+            .unbounded_send(SchedulerMsg::TaskNotified(arc_self.id))
+            .unwrap();
     }
 }

+ 87 - 81
packages/core/src/scheduler/wait.rs

@@ -1,3 +1,4 @@
+use futures_util::task::ArcWake;
 use futures_util::FutureExt;
 use std::{
     rc::Rc,
@@ -10,7 +11,7 @@ use crate::{
     ScopeId, TaskId, VNode, VirtualDom,
 };
 
-use super::{waker::ArcWake, SuspenseId};
+use super::SuspenseId;
 
 impl VirtualDom {
     /// Handle notifications by tasks inside the scheduler
@@ -20,22 +21,27 @@ impl VirtualDom {
     pub(crate) fn handle_task_wakeup(&mut self, id: TaskId) {
         let mut tasks = self.scheduler.tasks.borrow_mut();
 
-        let task = match tasks.get(id.0) {
-            Some(task) => task,
-            None => return,
-        };
-
-        let waker = task.waker();
-        let mut cx = Context::from_waker(&waker);
-
-        // If the task completes...
-        if task.task.borrow_mut().as_mut().poll(&mut cx).is_ready() {
-            // Remove it from the scope so we dont try to double drop it when the scope dropes
-            self.scopes[task.scope.0].spawned_tasks.remove(&id);
-
-            // Remove it from the scheduler
-            tasks.remove(id.0);
-        }
+        // let task = match tasks.get(id.0) {
+        //     Some(task) => task,
+        //     None => {
+        //         // The task was removed from the scheduler, so we can just ignore it
+        //         return;
+        //     }
+        // };
+
+        // let mut cx = Context::from_waker(&task.waker);
+
+        // // If the task completes...
+        // println!("polling task");
+        // if task.task.borrow_mut().as_mut().poll(&mut cx).is_ready() {
+        //     // Remove it from the scope so we dont try to double drop it when the scope dropes
+        //     self.scopes[task.scope.0].spawned_tasks.remove(&id);
+
+        //     // Remove it from the scheduler
+        //     tasks.remove(id.0);
+        // } else {
+        //     println!("task not ready yet, but we gave it a handle to the waker");
+        // }
     }
 
     pub(crate) fn acquire_suspense_boundary(&self, id: ScopeId) -> Rc<SuspenseContext> {
@@ -45,69 +51,69 @@ impl VirtualDom {
     }
 
     pub(crate) fn handle_suspense_wakeup(&mut self, id: SuspenseId) {
-        let leaf = self
-            .scheduler
-            .leaves
-            .borrow_mut()
-            .get(id.0)
-            .unwrap()
-            .clone();
-
-        let scope_id = leaf.scope_id;
-
-        // todo: cache the waker
-        let waker = leaf.waker();
-        let mut cx = Context::from_waker(&waker);
-
-        // Safety: the future is always pinned to the bump arena
-        let mut pinned = unsafe { std::pin::Pin::new_unchecked(&mut *leaf.task) };
-        let as_pinned_mut = &mut pinned;
-
-        // the component finished rendering and gave us nodes
-        // we should attach them to that component and then render its children
-        // continue rendering the tree until we hit yet another suspended component
-        if let Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx) {
-            let fiber = self.acquire_suspense_boundary(leaf.scope_id);
-
-            let scope = &mut self.scopes[scope_id.0];
-            let arena = scope.current_frame();
-
-            let ret = arena.bump.alloc(match new_nodes {
-                Some(new) => RenderReturn::Ready(new),
-                None => RenderReturn::default(),
-            });
-
-            arena.node.set(ret);
-
-            fiber.waiting_on.borrow_mut().remove(&id);
-
-            if let RenderReturn::Ready(template) = ret {
-                let mutations_ref = &mut fiber.mutations.borrow_mut();
-                let mutations = &mut **mutations_ref;
-                let template: &VNode = unsafe { std::mem::transmute(template) };
-                let mutations: &mut Mutations = unsafe { std::mem::transmute(mutations) };
-
-                std::mem::swap(&mut self.mutations, mutations);
-
-                let place_holder_id = scope.placeholder.get().unwrap();
-                self.scope_stack.push(scope_id);
-                let created = self.create(template);
-                self.scope_stack.pop();
-                mutations.push(Mutation::ReplaceWith {
-                    id: place_holder_id,
-                    m: created,
-                });
-
-                for leaf in self.collected_leaves.drain(..) {
-                    fiber.waiting_on.borrow_mut().insert(leaf);
-                }
-
-                std::mem::swap(&mut self.mutations, mutations);
-
-                if fiber.waiting_on.borrow().is_empty() {
-                    self.finished_fibers.push(fiber.id);
-                }
-            }
-        }
+        // let leaf = self
+        //     .scheduler
+        //     .leaves
+        //     .borrow_mut()
+        //     .get(id.0)
+        //     .unwrap()
+        //     .clone();
+
+        // let scope_id = leaf.scope_id;
+
+        // // todo: cache the waker
+        // let waker = leaf.waker();
+        // let mut cx = Context::from_waker(&waker);
+
+        // // Safety: the future is always pinned to the bump arena
+        // let mut pinned = unsafe { std::pin::Pin::new_unchecked(&mut *leaf.task) };
+        // let as_pinned_mut = &mut pinned;
+
+        // // the component finished rendering and gave us nodes
+        // // we should attach them to that component and then render its children
+        // // continue rendering the tree until we hit yet another suspended component
+        // if let Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx) {
+        //     let fiber = self.acquire_suspense_boundary(leaf.scope_id);
+
+        //     let scope = &mut self.scopes[scope_id.0];
+        //     let arena = scope.current_frame();
+
+        //     let ret = arena.bump.alloc(match new_nodes {
+        //         Some(new) => RenderReturn::Ready(new),
+        //         None => RenderReturn::default(),
+        //     });
+
+        //     arena.node.set(ret);
+
+        //     fiber.waiting_on.borrow_mut().remove(&id);
+
+        //     if let RenderReturn::Ready(template) = ret {
+        //         let mutations_ref = &mut fiber.mutations.borrow_mut();
+        //         let mutations = &mut **mutations_ref;
+        //         let template: &VNode = unsafe { std::mem::transmute(template) };
+        //         let mutations: &mut Mutations = unsafe { std::mem::transmute(mutations) };
+
+        //         std::mem::swap(&mut self.mutations, mutations);
+
+        //         let place_holder_id = scope.placeholder.get().unwrap();
+        //         self.scope_stack.push(scope_id);
+        //         let created = self.create(template);
+        //         self.scope_stack.pop();
+        //         mutations.push(Mutation::ReplaceWith {
+        //             id: place_holder_id,
+        //             m: created,
+        //         });
+
+        //         for leaf in self.collected_leaves.drain(..) {
+        //             fiber.waiting_on.borrow_mut().insert(leaf);
+        //         }
+
+        //         std::mem::swap(&mut self.mutations, mutations);
+
+        //         if fiber.waiting_on.borrow().is_empty() {
+        //             self.finished_fibers.push(fiber.id);
+        //         }
+        //     }
+        // }
     }
 }

+ 0 - 37
packages/core/src/scheduler/waker.rs

@@ -1,37 +0,0 @@
-use std::mem;
-use std::sync::Arc;
-use std::task::{RawWaker, RawWakerVTable, Waker};
-
-pub trait ArcWake: Sized {
-    /// Create a waker from this self-wakening object
-    fn waker(self: &Arc<Self>) -> Waker {
-        unsafe fn rc_vtable<T: ArcWake>() -> &'static RawWakerVTable {
-            &RawWakerVTable::new(
-                |data| {
-                    let arc = mem::ManuallyDrop::new(Arc::<T>::from_raw(data.cast::<T>()));
-                    let _rc_clone: mem::ManuallyDrop<_> = arc.clone();
-                    RawWaker::new(data, rc_vtable::<T>())
-                },
-                |data| Arc::from_raw(data.cast::<T>()).wake(),
-                |data| {
-                    let arc = mem::ManuallyDrop::new(Arc::<T>::from_raw(data.cast::<T>()));
-                    ArcWake::wake_by_ref(&arc);
-                },
-                |data| drop(Arc::<T>::from_raw(data.cast::<T>())),
-            )
-        }
-
-        unsafe {
-            Waker::from_raw(RawWaker::new(
-                Arc::into_raw(self.clone()).cast(),
-                rc_vtable::<Self>(),
-            ))
-        }
-    }
-
-    fn wake_by_ref(arc_self: &Arc<Self>);
-
-    fn wake(self: Arc<Self>) {
-        Self::wake_by_ref(&self)
-    }
-}

+ 8 - 5
packages/core/src/scope_arena.rs

@@ -2,9 +2,8 @@ use crate::{
     any_props::AnyProps,
     bump_frame::BumpFrame,
     innerlude::DirtyScope,
-    innerlude::{SuspenseId, SuspenseLeaf},
+    innerlude::{SuspenseHandle, SuspenseId, SuspenseLeaf},
     nodes::RenderReturn,
-    scheduler::ArcWake,
     scopes::{ScopeId, ScopeState},
     virtual_dom::VirtualDom,
 };
@@ -90,15 +89,19 @@ impl VirtualDom {
             let entry = leaves.vacant_entry();
             let suspense_id = SuspenseId(entry.key());
 
-            let leaf = Arc::new(SuspenseLeaf {
+            let leaf = SuspenseLeaf {
                 scope_id,
                 task: task.as_mut(),
                 id: suspense_id,
                 tx: self.scheduler.sender.clone(),
                 notified: Default::default(),
-            });
+                waker: Arc::new(SuspenseHandle {
+                    id: suspense_id,
+                    tx: self.scheduler.sender.clone(),
+                }),
+            };
 
-            let waker = leaf.waker();
+            let waker = futures_util::task::waker(leaf.waker.clone());
             let mut cx = Context::from_waker(&waker);
 
             // safety: the task is already pinned in the bump arena

+ 22 - 5
packages/core/src/virtual_dom.rs

@@ -13,10 +13,21 @@ use crate::{
     scopes::{ScopeId, ScopeState},
     AttributeValue, Element, Event, Scope, SuspenseContext,
 };
-use futures_util::{pin_mut, StreamExt};
+use futures_util::{
+    pin_mut,
+    stream::{futures_unordered, FuturesUnordered},
+    StreamExt,
+};
 use rustc_hash::FxHashMap;
 use slab::Slab;
-use std::{any::Any, borrow::BorrowMut, cell::Cell, collections::BTreeSet, future::Future, rc::Rc};
+use std::{
+    any::Any,
+    borrow::BorrowMut,
+    cell::Cell,
+    collections::{BTreeSet, HashSet},
+    future::Future,
+    rc::Rc,
+};
 
 /// A virtual node system that progresses user events and diffs UI trees.
 ///
@@ -420,7 +431,7 @@ impl VirtualDom {
         loop {
             match some_msg.take() {
                 // If a bunch of messages are ready in a sequence, try to pop them off synchronously
-                Some(msg) => match msg {
+                Some(msg) => match dbg!(msg) {
                     SchedulerMsg::Immediate(id) => self.mark_dirty(id),
                     SchedulerMsg::TaskNotified(task) => self.handle_task_wakeup(task),
                     SchedulerMsg::SuspenseNotified(id) => self.handle_suspense_wakeup(id),
@@ -430,14 +441,20 @@ impl VirtualDom {
                 None => {
                     match self.rx.try_next() {
                         Ok(Some(val)) => some_msg = Some(val),
-                        Ok(None) => return,
+                        Ok(None) => panic!("Scheduler channel closed"),
                         Err(_) => {
                             // If we have any dirty scopes, or finished fiber trees then we should exit
                             if !self.dirty_scopes.is_empty() || !self.finished_fibers.is_empty() {
                                 return;
                             }
 
-                            some_msg = self.rx.next().await
+                            println!("Waiting for next message...");
+                            let mut tasks = self.scheduler.tasks.borrow_mut();
+
+                            let takss = &mut *tasks;
+
+                            use futures_util::StreamExt;
+                            takss.next().await;
                         }
                     }
                 }

+ 30 - 91
packages/desktop/src/desktop_context.rs

@@ -13,6 +13,8 @@ use wry::application::event_loop::EventLoopProxy;
 #[cfg(target_os = "ios")]
 use wry::application::platform::ios::WindowExtIOS;
 use wry::application::window::Fullscreen as WryFullscreen;
+use wry::application::window::Window;
+use wry::webview::WebView;
 
 use UserWindowEvent::*;
 
@@ -40,16 +42,32 @@ pub fn use_window(cx: &ScopeState) -> &DesktopContext {
 #[derive(Clone)]
 pub struct DesktopContext {
     /// The wry/tao proxy to the current window
+    pub webview: Rc<WebView>,
+
+    /// The proxy to the event loop
     pub proxy: ProxyType,
+
+    /// The receiver for eval results since eval is async
     pub(super) eval_reciever: Rc<tokio::sync::Mutex<tokio::sync::mpsc::UnboundedReceiver<Value>>>,
 }
 
+/// A smart pointer to the current window.
+impl std::ops::Deref for DesktopContext {
+    type Target = Window;
+
+    fn deref(&self) -> &Self::Target {
+        &self.webview.window()
+    }
+}
+
 impl DesktopContext {
     pub(crate) fn new(
+        webview: Rc<WebView>,
         proxy: ProxyType,
         eval_reciever: tokio::sync::mpsc::UnboundedReceiver<Value>,
     ) -> Self {
         Self {
+            webview,
             proxy,
             eval_reciever: Rc::new(tokio::sync::Mutex::new(eval_reciever)),
         }
@@ -66,82 +84,21 @@ impl DesktopContext {
     pub fn drag(&self) {
         let _ = self.proxy.send_event(DragWindow);
     }
-
-    /// set window minimize state
-    pub fn set_minimized(&self, minimized: bool) {
-        let _ = self.proxy.send_event(Minimize(minimized));
-    }
-
-    /// set window maximize state
-    pub fn set_maximized(&self, maximized: bool) {
-        let _ = self.proxy.send_event(Maximize(maximized));
-    }
-
     /// toggle window maximize state
     pub fn toggle_maximized(&self) {
         let _ = self.proxy.send_event(MaximizeToggle);
     }
 
-    /// set window visible or not
-    pub fn set_visible(&self, visible: bool) {
-        let _ = self.proxy.send_event(Visible(visible));
-    }
-
     /// close window
     pub fn close(&self) {
         let _ = self.proxy.send_event(CloseWindow);
     }
 
-    /// set window to focus
-    pub fn focus(&self) {
-        let _ = self.proxy.send_event(FocusWindow);
-    }
-
     /// change window to fullscreen
     pub fn set_fullscreen(&self, fullscreen: bool) {
         let _ = self.proxy.send_event(Fullscreen(fullscreen));
     }
 
-    /// set resizable state
-    pub fn set_resizable(&self, resizable: bool) {
-        let _ = self.proxy.send_event(Resizable(resizable));
-    }
-
-    /// set the window always on top
-    pub fn set_always_on_top(&self, top: bool) {
-        let _ = self.proxy.send_event(AlwaysOnTop(top));
-    }
-
-    /// set cursor visible or not
-    pub fn set_cursor_visible(&self, visible: bool) {
-        let _ = self.proxy.send_event(CursorVisible(visible));
-    }
-
-    /// set cursor grab
-    pub fn set_cursor_grab(&self, grab: bool) {
-        let _ = self.proxy.send_event(CursorGrab(grab));
-    }
-
-    /// set window title
-    pub fn set_title(&self, title: &str) {
-        let _ = self.proxy.send_event(SetTitle(String::from(title)));
-    }
-
-    /// change window to borderless
-    pub fn set_decorations(&self, decoration: bool) {
-        let _ = self.proxy.send_event(SetDecorations(decoration));
-    }
-
-    /// set window zoom level
-    pub fn set_zoom_level(&self, scale_factor: f64) {
-        let _ = self.proxy.send_event(SetZoomLevel(scale_factor));
-    }
-
-    /// modifies the inner size of the window
-    pub fn set_inner_size(&self, logical_size: LogicalSize<f64>) {
-        let _ = self.proxy.send_event(SetInnerSize(logical_size));
-    }
-
     /// launch print modal
     pub fn print(&self) {
         let _ = self.proxy.send_event(Print);
@@ -177,31 +134,20 @@ pub enum UserWindowEvent {
 
     Poll,
 
+    UserEvent(serde_json::Value),
+
     CloseWindow,
     DragWindow,
-    FocusWindow,
 
-    Visible(bool),
-    Minimize(bool),
-    Maximize(bool),
     MaximizeToggle,
-    Resizable(bool),
-    AlwaysOnTop(bool),
-    Fullscreen(bool),
 
-    CursorVisible(bool),
-    CursorGrab(bool),
-
-    SetTitle(String),
-    SetDecorations(bool),
-
-    SetZoomLevel(f64),
-    SetInnerSize(LogicalSize<f64>),
+    Fullscreen(bool),
 
     Print,
     DevTool,
 
     Eval(String),
+    EvalResult(serde_json::Value),
 
     #[cfg(target_os = "ios")]
     PushView(objc_id::ShareId<objc::runtime::Object>),
@@ -236,18 +182,17 @@ impl DesktopController {
                 // if the drag_window has any errors, we don't do anything
                 window.fullscreen().is_none().then(|| window.drag_window());
             }
-            Visible(state) => window.set_visible(state),
-            Minimize(state) => window.set_minimized(state),
-            Maximize(state) => window.set_maximized(state),
+
             MaximizeToggle => window.set_maximized(!window.is_maximized()),
             Fullscreen(state) => {
                 if let Some(handle) = window.current_monitor() {
                     window.set_fullscreen(state.then_some(WryFullscreen::Borderless(Some(handle))));
                 }
             }
-            FocusWindow => window.set_focus(),
-            Resizable(state) => window.set_resizable(state),
-            AlwaysOnTop(state) => window.set_always_on_top(state),
+
+            UserEvent(event) => {
+                //
+            }
 
             Eval(code) => {
                 let script = format!(
@@ -261,16 +206,10 @@ impl DesktopController {
                     log::warn!("Eval script error: {e}");
                 }
             }
-            CursorVisible(state) => window.set_cursor_visible(state),
-            CursorGrab(state) => {
-                let _ = window.set_cursor_grab(state);
-            }
-
-            SetTitle(content) => window.set_title(&content),
-            SetDecorations(state) => window.set_decorations(state),
 
-            SetZoomLevel(scale_factor) => webview.zoom(scale_factor),
-            SetInnerSize(logical_size) => window.set_inner_size(logical_size),
+            EvalResult(result) => {
+                // todo
+            }
 
             Poll => {
                 // todo

+ 80 - 50
packages/desktop/src/lib.rs

@@ -13,6 +13,7 @@ mod protocol;
 #[cfg(all(feature = "hot-reload", debug_assertions))]
 mod hot_reload;
 
+use dioxus_html::{a, HtmlEvent};
 use futures_util::task::ArcWake;
 use serde_json::Value;
 use std::cell::RefCell;
@@ -115,23 +116,18 @@ pub async fn launch_cfg(root: Component, config_builder: Config) {
 /// }
 /// ```
 pub async fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cfg: Config) {
-    let mut event_loop = EventLoop::with_user_event();
+    let event_loop = EventLoop::with_user_event();
 
-    let is_ready = Arc::new(AtomicBool::new(false));
-    let (eval_sender, eval_reciever) = tokio::sync::mpsc::unbounded_channel();
-
-    let mut dom = VirtualDom::new_with_props(root, props).with_root_context(DesktopContext::new(
-        event_loop.create_proxy(),
-        eval_reciever,
-    ));
+    let mut dom = VirtualDom::new_with_props(root, props);
 
     let proxy = event_loop.create_proxy();
 
+    // We want to poll the virtualdom and the event loop at the same time
+    // So the waker will be connected to both
     let waker = futures_util::task::waker(Arc::new(DomHandle {
         proxy: proxy.clone(),
     }));
 
-    let mut events = Rc::new(RefCell::new(vec![]));
     let mut webviews = HashMap::new();
 
     event_loop.run(move |window_event, event_loop, control_flow| {
@@ -139,20 +135,21 @@ pub async fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P,
 
         match window_event {
             Event::NewEvents(StartCause::Init) => {
-                let window = build_webview(
-                    &mut cfg,
-                    event_loop,
-                    is_ready.clone(),
-                    proxy.clone(),
-                    eval_sender.clone(),
-                    events.clone(),
-                );
-
-                webviews.insert(window.window().id(), window);
-
-                // desktop.start(&mut cfg, event_loop);
+                let (eval_sender, eval_reciever) = tokio::sync::mpsc::unbounded_channel();
+                let window = Rc::new(build_webview(&mut cfg, event_loop, proxy.clone()));
+                let ctx = DesktopContext::new(window.clone(), proxy.clone(), eval_reciever);
+                dom.base_scope().provide_context(ctx);
+                webviews.insert(window.window().id(), window.clone());
+
+                proxy.send_event(UserWindowEvent::Poll).unwrap();
             }
 
+            Event::MainEventsCleared => {}
+            Event::Resumed => {}
+            Event::Suspended => {}
+            Event::LoopDestroyed => {}
+            Event::RedrawRequested(_id) => {}
+
             Event::WindowEvent { event, .. } => match event {
                 WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
                 WindowEvent::Destroyed { .. } => {
@@ -165,19 +162,65 @@ pub async fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P,
                 println!("user event: {:?}", user_event);
 
                 match user_event {
+                    UserWindowEvent::UserEvent(json_value) => {
+                        if let Ok(value) = serde_json::from_value::<HtmlEvent>(json_value) {
+                            let HtmlEvent {
+                                name,
+                                element,
+                                bubbles,
+                                data,
+                            } = value;
+
+                            dom.handle_event(&name, data.into_any(), element, bubbles);
+
+                            let edits = dom.render_immediate();
+
+                            let serialized = serde_json::to_string(&edits).unwrap();
+
+                            let (_id, view) = webviews.iter_mut().next().unwrap();
+
+                            view.evaluate_script(&format!(
+                                "window.interpreter.handleEdits({})",
+                                serialized
+                            ))
+                            .unwrap();
+                        }
+                    }
+
                     UserWindowEvent::Poll => {
                         let mut cx = std::task::Context::from_waker(&waker);
 
-                        let render = {
-                            let fut = dom.wait_for_work();
-                            pin_mut!(fut);
-                            matches!(fut.poll_unpin(&mut cx), std::task::Poll::Ready(_))
-                        };
+                        println!("polling..");
+
+                        loop {
+                            {
+                                println!("wait for next work");
+
+                                let fut = dom.wait_for_work();
+                                pin_mut!(fut);
+
+                                match fut.poll_unpin(&mut cx) {
+                                    std::task::Poll::Ready(_) => {
+                                        println!("work ready");
+                                    }
+                                    std::task::Poll::Pending => break,
+                                }
+                            }
+
+                            println!("rendering..");
 
-                        if render {
                             let edits = dom.render_immediate();
 
                             // apply the edits
+                            let serialized = serde_json::to_string(&edits).unwrap();
+
+                            let (_id, view) = webviews.iter_mut().next().unwrap();
+
+                            view.evaluate_script(&format!(
+                                "window.interpreter.handleEdits({})",
+                                serialized
+                            ))
+                            .unwrap();
                         }
                     }
 
@@ -200,11 +243,7 @@ pub async fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P,
                     }
                 }
             }
-            Event::MainEventsCleared => {}
-            Event::Resumed => {}
-            Event::Suspended => {}
-            Event::LoopDestroyed => {}
-            Event::RedrawRequested(_id) => {}
+
             _ => {}
         }
     })
@@ -223,10 +262,7 @@ impl ArcWake for DomHandle {
 fn build_webview(
     cfg: &mut Config,
     event_loop: &tao::event_loop::EventLoopWindowTarget<UserWindowEvent>,
-    is_ready: Arc<AtomicBool>,
     proxy: tao::event_loop::EventLoopProxy<UserWindowEvent>,
-    eval_sender: tokio::sync::mpsc::UnboundedSender<serde_json::Value>,
-    event_tx: Rc<RefCell<Vec<Value>>>,
 ) -> wry::webview::WebView {
     let builder = cfg.window.clone();
     let window = builder.build(event_loop).unwrap();
@@ -264,29 +300,23 @@ fn build_webview(
 
             match message.method() {
                 "eval_result" => {
-                    let result = message.params();
-                    eval_sender.send(result).unwrap();
+                    let _ = proxy.send_event(UserWindowEvent::EvalResult(message.params()));
                 }
                 "user_event" => {
-                    _ = event_tx.borrow_mut().push(message.params());
-                    let _ = proxy.send_event(UserWindowEvent::Poll);
+                    let _ = proxy.send_event(UserWindowEvent::UserEvent(message.params()));
                 }
                 "initialize" => {
-                    is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
                     let _ = proxy.send_event(UserWindowEvent::EditsReady);
                 }
-                "browser_open" => {
-                    let data = message.params();
-                    log::trace!("Open browser: {:?}", data);
-                    if let Some(temp) = data.as_object() {
-                        if temp.contains_key("href") {
-                            let url = temp.get("href").unwrap().as_str().unwrap();
-                            if let Err(e) = webbrowser::open(url) {
-                                log::error!("Open Browser error: {:?}", e);
-                            }
+                "browser_open" => match message.params().as_object() {
+                    Some(temp) if temp.contains_key("href") => {
+                        let open = webbrowser::open(temp["href"].as_str().unwrap());
+                        if let Err(e) = open {
+                            log::error!("Open Browser error: {:?}", e);
                         }
                     }
-                }
+                    _ => (),
+                },
                 _ => (),
             }
         })