@@ -112,24 +112,39 @@ impl EventHandler {
112112 matches ! ( self . inner. try_borrow( ) . as_deref( ) , Ok ( Some ( _) ) )
113113 }
114114
115- pub fn handle ( & self , callback : impl FnOnce ( & mut ( dyn ApplicationHandler + ' _ ) ) ) {
115+ /// Try to call the handler and return a value.
116+ ///
117+ /// Returns `None` if the handler is not set or is currently in use (re-entrant call).
118+ ///
119+ /// It is important that we keep the `RefMut` borrowed during the callback, so that `in_use`
120+ /// can properly detect that the handler is still in use. If the handler unwinds, the `RefMut`
121+ /// will ensure that the handler is no longer borrowed.
122+ pub fn handle_with_result < R > (
123+ & self ,
124+ callback : impl FnOnce ( & mut ( dyn ApplicationHandler + ' _ ) ) -> R ,
125+ ) -> Option < R > {
116126 match self . inner . try_borrow_mut ( ) . as_deref_mut ( ) {
117- Ok ( Some ( user_app) ) => {
118- // It is important that we keep the reference borrowed here,
119- // so that `in_use` can properly detect that the handler is
120- // still in use.
121- //
122- // If the handler unwinds, the `RefMut` will ensure that the
123- // handler is no longer borrowed.
124- callback ( & mut * * user_app) ;
125- } ,
127+ Ok ( Some ( user_app) ) => Some ( callback ( & mut * * user_app) ) ,
126128 Ok ( None ) => {
127- // `NSApplication`, our app state and this handler are all
128- // global state and so it's not impossible that we could get
129- // an event after the application has exited the `EventLoop`.
129+ // `NSApplication`, our app state and this handler are all global state and so
130+ // it's not impossible that we could get an event after the application has
131+ // exited the `EventLoop`.
130132 tracing:: error!( "tried to run event handler, but no handler was set" ) ;
133+ None
131134 } ,
132135 Err ( _) => {
136+ // Handler is currently in use, return None instead of panicking.
137+ None
138+ } ,
139+ }
140+ }
141+
142+ pub fn handle ( & self , callback : impl FnOnce ( & mut ( dyn ApplicationHandler + ' _ ) ) ) {
143+ match self . handle_with_result ( callback) {
144+ Some ( ( ) ) => { } ,
145+ // Handler not set — already logged by handle_with_result.
146+ None if !self . in_use ( ) => { } ,
147+ None => {
133148 // Prevent re-entrancy.
134149 panic ! ( "tried to handle event while another event is currently being handled" ) ;
135150 } ,
@@ -153,3 +168,82 @@ impl EventHandler {
153168 }
154169 }
155170}
171+
172+ #[ cfg( test) ]
173+ mod tests {
174+ use winit_core:: application:: ApplicationHandler ;
175+ use winit_core:: event:: WindowEvent ;
176+ use winit_core:: event_loop:: ActiveEventLoop ;
177+ use winit_core:: window:: WindowId ;
178+
179+ use super :: EventHandler ;
180+
181+ struct DummyApp ;
182+
183+ impl ApplicationHandler for DummyApp {
184+ fn can_create_surfaces ( & mut self , _event_loop : & dyn ActiveEventLoop ) { }
185+
186+ fn window_event (
187+ & mut self ,
188+ _event_loop : & dyn ActiveEventLoop ,
189+ _window_id : WindowId ,
190+ _event : WindowEvent ,
191+ ) {
192+ }
193+ }
194+
195+ #[ test]
196+ fn handle_with_result_returns_value ( ) {
197+ let handler = EventHandler :: new ( ) ;
198+ handler. set ( Box :: new ( DummyApp ) , || {
199+ let result = handler. handle_with_result ( |_app| 42 ) ;
200+ assert_eq ! ( result, Some ( 42 ) ) ;
201+ } ) ;
202+ }
203+
204+ #[ test]
205+ fn handle_with_result_returns_none_when_not_set ( ) {
206+ let handler = EventHandler :: new ( ) ;
207+ let result = handler. handle_with_result ( |_app| 42 ) ;
208+ assert_eq ! ( result, None ) ;
209+ }
210+
211+ #[ test]
212+ fn handle_with_result_returns_none_when_in_use ( ) {
213+ let handler = EventHandler :: new ( ) ;
214+ handler. set ( Box :: new ( DummyApp ) , || {
215+ // Borrow the handler via `handle`, then try `handle_with_result`
216+ // from within — simulating re-entrancy.
217+ handler. handle ( |_app| {
218+ let result = handler. handle_with_result ( |_app| 42 ) ;
219+ assert_eq ! ( result, None ) ;
220+ } ) ;
221+ } ) ;
222+ }
223+
224+ #[ test]
225+ fn handle_with_result_returns_none_when_reentrant_through_self ( ) {
226+ let handler = EventHandler :: new ( ) ;
227+ handler. set ( Box :: new ( DummyApp ) , || {
228+ let result = handler. handle_with_result ( |_app| {
229+ // Re-entrant call through handle_with_result itself.
230+ handler. handle_with_result ( |_app| 42 )
231+ } ) ;
232+ assert_eq ! ( result, Some ( None ) ) ;
233+ } ) ;
234+ }
235+
236+ #[ test]
237+ #[ should_panic(
238+ expected = "tried to handle event while another event is currently being handled"
239+ ) ]
240+ fn handle_panics_on_reentrant_call ( ) {
241+ let handler = EventHandler :: new ( ) ;
242+ handler. set ( Box :: new ( DummyApp ) , || {
243+ handler. handle ( |_app| {
244+ // Re-entrant handle must still panic after the refactoring.
245+ handler. handle ( |_app| { } ) ;
246+ } ) ;
247+ } ) ;
248+ }
249+ }
0 commit comments