Skip to content

Commit 9a0b42c

Browse files
authored
defer certain events on macOS to avoid re-entrant calls (#189)
* defer certain events on macOS to avoid re-entrant calls * immediately trigger deferred event if borrowing succeeds * nicer signature * rename method: defer_event -> trigger_deferrable_event * clean up, add comments * address review comments * fix borrow logic when sending deferred events * handle deferred events after on_frame
1 parent 21fdc89 commit 9a0b42c

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

src/macos/view.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,14 @@ extern "C" fn become_first_responder(this: &Object, _sel: Sel) -> BOOL {
249249
is_key_window == YES
250250
};
251251
if is_key_window {
252-
state.trigger_event(Event::Window(WindowEvent::Focused));
252+
state.trigger_deferrable_event(Event::Window(WindowEvent::Focused));
253253
}
254254
YES
255255
}
256256

257257
extern "C" fn resign_first_responder(this: &Object, _sel: Sel) -> BOOL {
258258
let state = unsafe { WindowState::from_view(this) };
259-
state.trigger_event(Event::Window(WindowEvent::Unfocused));
259+
state.trigger_deferrable_event(Event::Window(WindowEvent::Unfocused));
260260
YES
261261
}
262262

src/macos/window.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::cell::{Cell, RefCell};
2+
use std::collections::VecDeque;
23
use std::ffi::c_void;
34
use std::ptr;
45
use std::rc::Rc;
@@ -267,6 +268,7 @@ impl<'a> Window<'a> {
267268
keyboard_state: KeyboardState::new(),
268269
frame_timer: Cell::new(None),
269270
window_info: Cell::new(window_info),
271+
deferred_events: RefCell::default(),
270272
});
271273

272274
let window_state_ptr = Rc::into_raw(Rc::clone(&window_state));
@@ -360,6 +362,9 @@ pub(super) struct WindowState {
360362
frame_timer: Cell<Option<CFRunLoopTimer>>,
361363
/// The last known window info for this window.
362364
pub window_info: Cell<WindowInfo>,
365+
366+
/// Events that will be triggered at the end of `window_handler`'s borrow.
367+
deferred_events: RefCell<VecDeque<Event>>,
363368
}
364369

365370
impl WindowState {
@@ -378,14 +383,34 @@ impl WindowState {
378383
state
379384
}
380385

386+
/// Trigger the event immediately and return the event status.
387+
/// Will panic if `window_handler` is already borrowed (see `trigger_deferrable_event`).
381388
pub(super) fn trigger_event(&self, event: Event) -> EventStatus {
382389
let mut window = crate::Window::new(Window { inner: &self.window_inner });
383-
self.window_handler.borrow_mut().on_event(&mut window, event)
390+
let mut window_handler = self.window_handler.borrow_mut();
391+
let status = window_handler.on_event(&mut window, event);
392+
self.send_deferred_events(window_handler.as_mut());
393+
status
394+
}
395+
396+
/// Trigger the event immediately if `window_handler` can be borrowed mutably,
397+
/// otherwise add the event to a queue that will be cleared once `window_handler`'s mutable borrow ends.
398+
/// As this method might result in the event triggering asynchronously, it can't reliably return the event status.
399+
pub(super) fn trigger_deferrable_event(&self, event: Event) {
400+
if let Ok(mut window_handler) = self.window_handler.try_borrow_mut() {
401+
let mut window = crate::Window::new(Window { inner: &self.window_inner });
402+
window_handler.on_event(&mut window, event);
403+
self.send_deferred_events(window_handler.as_mut());
404+
} else {
405+
self.deferred_events.borrow_mut().push_back(event);
406+
}
384407
}
385408

386409
pub(super) fn trigger_frame(&self) {
387410
let mut window = crate::Window::new(Window { inner: &self.window_inner });
388-
self.window_handler.borrow_mut().on_frame(&mut window);
411+
let mut window_handler = self.window_handler.borrow_mut();
412+
window_handler.on_frame(&mut window);
413+
self.send_deferred_events(window_handler.as_mut());
389414
}
390415

391416
pub(super) fn keyboard_state(&self) -> &KeyboardState {
@@ -419,6 +444,18 @@ impl WindowState {
419444

420445
(*window_state_ptr).frame_timer.set(Some(timer));
421446
}
447+
448+
fn send_deferred_events(&self, window_handler: &mut dyn WindowHandler) {
449+
let mut window = crate::Window::new(Window { inner: &self.window_inner });
450+
loop {
451+
let next_event = self.deferred_events.borrow_mut().pop_front();
452+
if let Some(event) = next_event {
453+
window_handler.on_event(&mut window, event);
454+
} else {
455+
break;
456+
}
457+
}
458+
}
422459
}
423460

424461
unsafe impl<'a> HasRawWindowHandle for Window<'a> {

0 commit comments

Comments
 (0)