11use std:: cell:: RefCell ;
2+ use std:: hash:: { Hash , Hasher } ;
23use std:: rc:: Rc ;
4+ use std:: sync:: Arc ;
5+ use std:: time:: Duration ;
36
47use iced:: keyboard;
58use iced:: mouse:: { self , Interaction } ;
69use iced:: { Point , Size } ;
10+ use tokio:: sync:: Notify ;
711
812use super :: { Engine , PageType , PixelFormat , ViewId , ViewManager } ;
913use crate :: ImageInfo ;
@@ -20,12 +24,35 @@ use servo::{
2024} ;
2125use url:: Url ;
2226
23- /// No-op waker — the iced subscription tick already drives `spin_event_loop`.
24- struct NoOpWaker ;
27+ /// Event-driven waker that Servo calls (from any thread) whenever it wants the
28+ /// embedder to spin its event loop. The `Notify` coalesces multiple wake signals
29+ /// into a single pending notification, so a burst of calls produces at most one
30+ /// wake-up on the consumer side.
31+ #[ derive( Clone ) ]
32+ struct ServoWaker {
33+ notify : Arc < Notify > ,
34+ }
2535
26- impl servo:: EventLoopWaker for NoOpWaker {
36+ impl servo:: EventLoopWaker for ServoWaker {
2737 fn clone_box ( & self ) -> Box < dyn servo:: EventLoopWaker > {
28- Box :: new ( NoOpWaker )
38+ Box :: new ( self . clone ( ) )
39+ }
40+
41+ fn wake ( & self ) {
42+ self . notify . notify_one ( ) ;
43+ }
44+ }
45+
46+ /// Hashable handle used to give the Servo wake subscription a stable identity
47+ /// in the iced runtime. Hashing the `Arc`'s pointer address is enough —
48+ /// each `Servo` instance has its own `Notify`, so two different engines get
49+ /// two distinct subscriptions.
50+ #[ derive( Clone ) ]
51+ struct WakeSubId ( Arc < Notify > ) ;
52+
53+ impl Hash for WakeSubId {
54+ fn hash < H : Hasher > ( & self , state : & mut H ) {
55+ ( Arc :: as_ptr ( & self . 0 ) as usize ) . hash ( state) ;
2956 }
3057}
3158
@@ -92,6 +119,10 @@ pub struct Servo {
92119 rendering_context : Rc < SoftwareRenderingContext > ,
93120 views : ViewManager < ServoView > ,
94121 scale_factor : f32 ,
122+ /// Shared with `ServoWaker` — Servo signals this whenever it wants the
123+ /// embedder to call `spin_event_loop`. The iced subscription exposed by
124+ /// [`Servo::subscription`] awaits the same handle.
125+ notify : Arc < Notify > ,
95126}
96127
97128impl Default for Servo {
@@ -101,19 +132,52 @@ impl Default for Servo {
101132 SoftwareRenderingContext :: new ( size) . expect ( "failed to create SoftwareRenderingContext" ) ;
102133 let rendering_context = Rc :: new ( rendering_context) ;
103134
135+ let notify = Arc :: new ( Notify :: new ( ) ) ;
136+ let waker = ServoWaker {
137+ notify : Arc :: clone ( & notify) ,
138+ } ;
139+
104140 let instance = ServoBuilder :: default ( )
105- . event_loop_waker ( Box :: new ( NoOpWaker ) )
141+ . event_loop_waker ( Box :: new ( waker ) )
106142 . build ( ) ;
107143
108144 Self {
109145 instance,
110146 rendering_context,
111147 views : ViewManager :: default ( ) ,
112148 scale_factor : 1.0 ,
149+ notify,
113150 }
114151 }
115152}
116153
154+ impl Servo {
155+ /// An iced [`Subscription`] that yields [`Action::Update`] whenever Servo
156+ /// signals it has work to do, plus a 500ms safety tick so the event loop
157+ /// still runs if a wake is somehow missed. This replaces the hardcoded
158+ /// `time::every(10ms)` pattern used for other engines.
159+ ///
160+ /// [`Subscription`]: iced::Subscription
161+ /// [`Action::Update`]: crate::Action::Update
162+ pub fn subscription ( & self ) -> iced:: Subscription < crate :: Action > {
163+ use iced:: futures:: SinkExt ;
164+
165+ let id = WakeSubId ( Arc :: clone ( & self . notify ) ) ;
166+
167+ let wake_stream = iced:: Subscription :: run_with ( id, |id| {
168+ let notify = Arc :: clone ( & id. 0 ) ;
169+ iced:: stream:: channel ( 1 , async move |mut output| loop {
170+ notify. notified ( ) . await ;
171+ let _ = output. send ( crate :: Action :: Update ) . await ;
172+ } )
173+ } ) ;
174+
175+ let fallback = iced:: time:: every ( Duration :: from_millis ( 500 ) ) . map ( |_| crate :: Action :: Update ) ;
176+
177+ iced:: Subscription :: batch ( [ wake_stream, fallback] )
178+ }
179+ }
180+
117181fn cursor_to_interaction ( cursor : Cursor ) -> Interaction {
118182 match cursor {
119183 Cursor :: Pointer => Interaction :: Pointer ,
0 commit comments