From 9229e2d88b7be23bc4416179afcfd9fdc82b9a52 Mon Sep 17 00:00:00 2001 From: Mads Marquart <mads@marquart.dk> Date: Sun, 23 Jan 2022 21:35:26 +0100 Subject: [PATCH] macOS RAII trace guards (#2150) * Add TraceGuard to make tracing simpler * Add SharedStateMutexGuard to make tracing simpler * Add trace_scope macro * Add missing let binding in trace_scope! --- src/platform_impl/macos/app_delegate.rs | 3 +- src/platform_impl/macos/mod.rs | 4 +- src/platform_impl/macos/util/async.rs | 19 +++-- src/platform_impl/macos/util/mod.rs | 35 ++++++-- src/platform_impl/macos/view.rs | 77 +++++++++-------- src/platform_impl/macos/window.rs | 96 ++++++++++++++++------ src/platform_impl/macos/window_delegate.rs | 77 ++++++----------- 7 files changed, 176 insertions(+), 135 deletions(-) diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index d9e7937f..6840497a 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -75,7 +75,6 @@ extern "C" fn dealloc(this: &Object, _: Sel) { } extern "C" fn did_finish_launching(this: &Object, _: Sel, _: id) { - trace!("Triggered `applicationDidFinishLaunching`"); + trace_scope!("applicationDidFinishLaunching:"); AppState::launched(this); - trace!("Completed `applicationDidFinishLaunching`"); } diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index 72daf36c..af2d384d 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -1,5 +1,8 @@ #![cfg(target_os = "macos")] +#[macro_use] +mod util; + mod app; mod app_delegate; mod app_state; @@ -9,7 +12,6 @@ mod ffi; mod menu; mod monitor; mod observer; -mod util; mod view; mod window; mod window_delegate; diff --git a/src/platform_impl/macos/util/async.rs b/src/platform_impl/macos/util/async.rs index 0c71bc8d..0bd4b7d8 100644 --- a/src/platform_impl/macos/util/async.rs +++ b/src/platform_impl/macos/util/async.rs @@ -14,7 +14,11 @@ use objc::runtime::{BOOL, NO}; use crate::{ dpi::LogicalSize, - platform_impl::platform::{ffi, util::IdRef, window::SharedState}, + platform_impl::platform::{ + ffi, + util::IdRef, + window::{SharedState, SharedStateMutexGuard}, + }, }; // Unsafe wrapper type that allows us to dispatch things that aren't Send. @@ -111,10 +115,11 @@ pub unsafe fn toggle_full_screen_async( if !curr_mask.contains(required) { set_style_mask(*ns_window, *ns_view, required); if let Some(shared_state) = shared_state.upgrade() { - trace!("Locked shared state in `toggle_full_screen_callback`"); - let mut shared_state_lock = shared_state.lock().unwrap(); + let mut shared_state_lock = SharedStateMutexGuard::new( + shared_state.lock().unwrap(), + "toggle_full_screen_callback", + ); (*shared_state_lock).saved_style = Some(curr_mask); - trace!("Unlocked shared state in `toggle_full_screen_callback`"); } } } @@ -144,8 +149,8 @@ pub unsafe fn set_maximized_async( let shared_state = MainThreadSafe(shared_state); Queue::main().exec_async(move || { if let Some(shared_state) = shared_state.upgrade() { - trace!("Locked shared state in `set_maximized`"); - let mut shared_state_lock = shared_state.lock().unwrap(); + let mut shared_state_lock = + SharedStateMutexGuard::new(shared_state.lock().unwrap(), "set_maximized"); // Save the standard frame sized if it is not zoomed if !is_zoomed { @@ -171,8 +176,6 @@ pub unsafe fn set_maximized_async( }; ns_window.setFrame_display_(new_rect, NO); } - - trace!("Unlocked shared state in `set_maximized`"); } }); } diff --git a/src/platform_impl/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs index b64155d9..b3e27439 100644 --- a/src/platform_impl/macos/util/mod.rs +++ b/src/platform_impl/macos/util/mod.rs @@ -11,7 +11,7 @@ use cocoa::{ foundation::{NSPoint, NSRect, NSString, NSUInteger}, }; use core_graphics::display::CGDisplay; -use objc::runtime::{Class, Object, Sel, BOOL, YES}; +use objc::runtime::{Class, Object}; use crate::dpi::LogicalPosition; use crate::platform_impl::platform::ffi; @@ -79,6 +79,35 @@ impl Clone for IdRef { } } +macro_rules! trace_scope { + ($s:literal) => { + let _crate = $crate::platform_impl::platform::util::TraceGuard::new(module_path!(), $s); + }; +} + +pub(crate) struct TraceGuard { + module_path: &'static str, + called_from_fn: &'static str, +} + +impl TraceGuard { + #[inline] + pub(crate) fn new(module_path: &'static str, called_from_fn: &'static str) -> Self { + trace!(target: module_path, "Triggered `{}`", called_from_fn); + Self { + module_path, + called_from_fn, + } + } +} + +impl Drop for TraceGuard { + #[inline] + fn drop(&mut self) { + trace!(target: self.module_path, "Completed `{}`", self.called_from_fn); + } +} + // For consistency with other platforms, this will... // 1. translate the bottom-left window corner into the top-left window corner // 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one @@ -129,10 +158,6 @@ pub unsafe fn open_emoji_picker() { let () = msg_send![NSApp(), orderFrontCharacterPalette: nil]; } -pub extern "C" fn yes(_: &Object, _: Sel) -> BOOL { - YES -} - pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, on: bool) { use cocoa::appkit::NSWindow; diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 20f18b64..192e4936 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -301,7 +301,7 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i } extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) { - trace!("Triggered `viewDidMoveToWindow`"); + trace_scope!("viewDidMoveToWindow"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -319,10 +319,10 @@ extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) { ]; state.tracking_rect = Some(tracking_rect); } - trace!("Completed `viewDidMoveToWindow`"); } extern "C" fn frame_did_change(this: &Object, _sel: Sel, _event: id) { + trace_scope!("frameDidChange:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -344,6 +344,7 @@ extern "C" fn frame_did_change(this: &Object, _sel: Sel, _event: id) { } extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) { + trace_scope!("drawRect:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -356,6 +357,7 @@ extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) { } extern "C" fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL { + trace_scope!("acceptsFirstResponder"); YES } @@ -363,10 +365,12 @@ extern "C" fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL { // IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem // TODO: Add an API extension for using `NSTouchBar` extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> BOOL { + trace_scope!("touchBar"); NO } extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) { + trace_scope!("resetCursorRects"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -386,20 +390,18 @@ extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) { } extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL { + trace_scope!("hasMarkedText"); unsafe { - trace!("Triggered `hasMarkedText`"); let marked_text: id = *this.get_ivar("markedText"); - trace!("Completed `hasMarkedText`"); (marked_text.length() > 0) as BOOL } } extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange { + trace_scope!("markedRange"); unsafe { - trace!("Triggered `markedRange`"); let marked_text: id = *this.get_ivar("markedText"); let length = marked_text.length(); - trace!("Completed `markedRange`"); if length > 0 { NSRange::new(0, length - 1) } else { @@ -409,8 +411,7 @@ extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange { } extern "C" fn selected_range(_this: &Object, _sel: Sel) -> NSRange { - trace!("Triggered `selectedRange`"); - trace!("Completed `selectedRange`"); + trace_scope!("selectedRange"); util::EMPTY_RANGE } @@ -421,7 +422,7 @@ extern "C" fn set_marked_text( _selected_range: NSRange, _replacement_range: NSRange, ) { - trace!("Triggered `setMarkedText`"); + trace_scope!("setMarkedText:selectedRange:replacementRange:"); unsafe { let marked_text_ref: &mut id = this.get_mut_ivar("markedText"); let _: () = msg_send![(*marked_text_ref), release]; @@ -434,11 +435,10 @@ extern "C" fn set_marked_text( }; *marked_text_ref = marked_text; } - trace!("Completed `setMarkedText`"); } extern "C" fn unmark_text(this: &Object, _sel: Sel) { - trace!("Triggered `unmarkText`"); + trace_scope!("unmarkText"); unsafe { let marked_text: id = *this.get_ivar("markedText"); let mutable_string = marked_text.mutableString(); @@ -448,12 +448,10 @@ extern "C" fn unmark_text(this: &Object, _sel: Sel) { let input_context: id = msg_send![this, inputContext]; let _: () = msg_send![input_context, discardMarkedText]; } - trace!("Completed `unmarkText`"); } extern "C" fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id { - trace!("Triggered `validAttributesForMarkedText`"); - trace!("Completed `validAttributesForMarkedText`"); + trace_scope!("validAttributesForMarkedText"); unsafe { msg_send![class!(NSArray), array] } } @@ -463,14 +461,12 @@ extern "C" fn attributed_substring_for_proposed_range( _range: NSRange, _actual_range: *mut c_void, // *mut NSRange ) -> id { - trace!("Triggered `attributedSubstringForProposedRange`"); - trace!("Completed `attributedSubstringForProposedRange`"); + trace_scope!("attributedSubstringForProposedRange:actualRange:"); nil } extern "C" fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger { - trace!("Triggered `characterIndexForPoint`"); - trace!("Completed `characterIndexForPoint`"); + trace_scope!("characterIndexForPoint:"); 0 } @@ -480,8 +476,8 @@ extern "C" fn first_rect_for_character_range( _range: NSRange, _actual_range: *mut c_void, // *mut NSRange ) -> NSRect { + trace_scope!("firstRectForCharacterRange:actualRange:"); unsafe { - trace!("Triggered `firstRectForCharacterRange`"); let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); let (x, y) = state.ime_spot.unwrap_or_else(|| { @@ -493,13 +489,12 @@ extern "C" fn first_rect_for_character_range( let y = util::bottom_left_to_top_left(content_rect); (x, y) }); - trace!("Completed `firstRectForCharacterRange`"); NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(0.0, 0.0)) } } extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) { - trace!("Triggered `insertText`"); + trace_scope!("insertText:replacementRange:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -530,11 +525,10 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran AppState::queue_events(events); } - trace!("Completed `insertText`"); } extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) { - trace!("Triggered `doCommandBySelector`"); + trace_scope!("doCommandBySelector:"); // Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character // happens, i.e. newlines, tabs, and Ctrl+C. unsafe { @@ -567,7 +561,6 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) { AppState::queue_events(events); } - trace!("Completed `doCommandBySelector`"); } fn get_characters(event: id, ignore_modifiers: bool) -> String { @@ -639,7 +632,7 @@ fn update_potentially_stale_modifiers(state: &mut ViewState, event: id) { } extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `keyDown`"); + trace_scope!("keyDown:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -694,11 +687,10 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { let _: () = msg_send![this, interpretKeyEvents: array]; } } - trace!("Completed `keyDown`"); } extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `keyUp`"); + trace_scope!("keyUp:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -725,11 +717,10 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `keyUp`"); } extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `flagsChanged`"); + trace_scope!("flagsChanged:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -786,10 +777,10 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) { event: WindowEvent::ModifiersChanged(state.modifiers), })); } - trace!("Completed `flagsChanged`"); } extern "C" fn insert_tab(this: &Object, _sel: Sel, _sender: id) { + trace_scope!("insertTab:"); unsafe { let window: id = msg_send![this, window]; let first_responder: id = msg_send![window, firstResponder]; @@ -801,6 +792,7 @@ extern "C" fn insert_tab(this: &Object, _sel: Sel, _sender: id) { } extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) { + trace_scope!("insertBackTab:"); unsafe { let window: id = msg_send![this, window]; let first_responder: id = msg_send![window, firstResponder]; @@ -814,7 +806,7 @@ extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) { // Allows us to receive Cmd-. (the shortcut for closing a dialog) // https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6 extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { - trace!("Triggered `cancelOperation`"); + trace_scope!("cancelOperation:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -844,7 +836,6 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `cancelOperation`"); } fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) { @@ -869,31 +860,37 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem } extern "C" fn mouse_down(this: &Object, _sel: Sel, event: id) { + trace_scope!("mouseDown:"); mouse_motion(this, event); mouse_click(this, event, MouseButton::Left, ElementState::Pressed); } extern "C" fn mouse_up(this: &Object, _sel: Sel, event: id) { + trace_scope!("mouseUp:"); mouse_motion(this, event); mouse_click(this, event, MouseButton::Left, ElementState::Released); } extern "C" fn right_mouse_down(this: &Object, _sel: Sel, event: id) { + trace_scope!("rightMouseDown:"); mouse_motion(this, event); mouse_click(this, event, MouseButton::Right, ElementState::Pressed); } extern "C" fn right_mouse_up(this: &Object, _sel: Sel, event: id) { + trace_scope!("rightMouseUp:"); mouse_motion(this, event); mouse_click(this, event, MouseButton::Right, ElementState::Released); } extern "C" fn other_mouse_down(this: &Object, _sel: Sel, event: id) { + trace_scope!("otherMouseDown:"); mouse_motion(this, event); mouse_click(this, event, MouseButton::Middle, ElementState::Pressed); } extern "C" fn other_mouse_up(this: &Object, _sel: Sel, event: id) { + trace_scope!("otherMouseUp:"); mouse_motion(this, event); mouse_click(this, event, MouseButton::Middle, ElementState::Released); } @@ -941,6 +938,8 @@ fn mouse_motion(this: &Object, event: id) { } } +// No tracing on these because that would be overly verbose + extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) { mouse_motion(this, event); } @@ -958,7 +957,7 @@ extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) { } extern "C" fn mouse_entered(this: &Object, _sel: Sel, _event: id) { - trace!("Triggered `mouseEntered`"); + trace_scope!("mouseEntered:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -972,11 +971,10 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, _event: id) { AppState::queue_event(EventWrapper::StaticEvent(enter_event)); } - trace!("Completed `mouseEntered`"); } extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) { - trace!("Triggered `mouseExited`"); + trace_scope!("mouseExited:"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); @@ -990,11 +988,10 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) { AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `mouseExited`"); } extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `scrollWheel`"); + trace_scope!("scrollWheel:"); mouse_motion(this, event); @@ -1043,11 +1040,10 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { AppState::queue_event(EventWrapper::StaticEvent(device_event)); AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `scrollWheel`"); } extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { - trace!("Triggered `pressureChangeWithEvent`"); + trace_scope!("pressureChangeWithEvent:"); mouse_motion(this, event); @@ -1069,16 +1065,17 @@ extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { AppState::queue_event(EventWrapper::StaticEvent(window_event)); } - trace!("Completed `pressureChangeWithEvent`"); } // Allows us to receive Ctrl-Tab and Ctrl-Esc. // Note that this *doesn't* help with any missing Cmd inputs. // https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816 extern "C" fn wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) -> BOOL { + trace_scope!("_wantsKeyDownForEvent:"); YES } extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL { + trace_scope!("acceptsFirstMouse:"); YES } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 57578535..2d6c14a9 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -2,11 +2,11 @@ use raw_window_handle::{AppKitHandle, RawWindowHandle}; use std::{ collections::VecDeque, convert::TryInto, - f64, + f64, ops, os::raw::c_void, sync::{ atomic::{AtomicBool, Ordering}, - Arc, Mutex, Weak, + Arc, Mutex, MutexGuard, Weak, }, }; @@ -251,13 +251,24 @@ lazy_static! { static ref WINDOW_CLASS: WindowClass = unsafe { let window_superclass = class!(NSWindow); let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap(); + + pub extern "C" fn can_become_main_window(_: &Object, _: Sel) -> BOOL { + trace_scope!("canBecomeMainWindow"); + YES + } + + pub extern "C" fn can_become_key_window(_: &Object, _: Sel) -> BOOL { + trace_scope!("canBecomeKeyWindow"); + YES + } + decl.add_method( sel!(canBecomeMainWindow), - util::yes as extern "C" fn(&Object, Sel) -> BOOL, + can_become_main_window as extern "C" fn(&Object, Sel) -> BOOL, ); decl.add_method( sel!(canBecomeKeyWindow), - util::yes as extern "C" fn(&Object, Sel) -> BOOL, + can_become_key_window as extern "C" fn(&Object, Sel) -> BOOL, ); WindowClass(decl.register()) }; @@ -311,11 +322,49 @@ impl From<WindowAttributes> for SharedState { } } +pub(crate) struct SharedStateMutexGuard<'a> { + guard: MutexGuard<'a, SharedState>, + called_from_fn: &'static str, +} + +impl<'a> SharedStateMutexGuard<'a> { + #[inline] + pub(crate) fn new(guard: MutexGuard<'a, SharedState>, called_from_fn: &'static str) -> Self { + trace!("Locked shared state in `{}`", called_from_fn); + Self { + guard, + called_from_fn, + } + } +} + +impl ops::Deref for SharedStateMutexGuard<'_> { + type Target = SharedState; + #[inline] + fn deref(&self) -> &Self::Target { + self.guard.deref() + } +} + +impl ops::DerefMut for SharedStateMutexGuard<'_> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.guard.deref_mut() + } +} + +impl Drop for SharedStateMutexGuard<'_> { + #[inline] + fn drop(&mut self) { + trace!("Unlocked shared state in `{}`", self.called_from_fn); + } +} + pub struct UnownedWindow { pub ns_window: IdRef, // never changes pub ns_view: IdRef, // never changes input_context: IdRef, // never changes - pub shared_state: Arc<Mutex<SharedState>>, + shared_state: Arc<Mutex<SharedState>>, decorations: AtomicBool, cursor_state: Weak<Mutex<CursorState>>, pub inner_rect: Option<PhysicalSize<u32>>, @@ -420,6 +469,14 @@ impl UnownedWindow { Ok((window, delegate)) } + #[track_caller] + pub(crate) fn lock_shared_state( + &self, + called_from_fn: &'static str, + ) -> SharedStateMutexGuard<'_> { + SharedStateMutexGuard::new(self.shared_state.lock().unwrap(), called_from_fn) + } + fn set_style_mask_async(&self, mask: NSWindowStyleMask) { unsafe { util::set_style_mask_async(*self.ns_window, *self.ns_view, mask) }; } @@ -530,10 +587,8 @@ impl UnownedWindow { #[inline] pub fn set_resizable(&self, resizable: bool) { let fullscreen = { - trace!("Locked shared state in `set_resizable`"); - let mut shared_state_lock = self.shared_state.lock().unwrap(); + let mut shared_state_lock = self.lock_shared_state("set_resizable"); shared_state_lock.resizable = resizable; - trace!("Unlocked shared state in `set_resizable`"); shared_state_lock.fullscreen.is_some() }; if !fullscreen { @@ -653,8 +708,7 @@ impl UnownedWindow { /// user clicking on the green fullscreen button or programmatically by /// `toggleFullScreen:` pub(crate) fn restore_state_from_fullscreen(&self) { - trace!("Locked shared state in `restore_state_from_fullscreen`"); - let mut shared_state_lock = self.shared_state.lock().unwrap(); + let mut shared_state_lock = self.lock_shared_state("restore_state_from_fullscreen"); shared_state_lock.fullscreen = None; @@ -662,7 +716,6 @@ impl UnownedWindow { let mask = self.saved_style(&mut *shared_state_lock); drop(shared_state_lock); - trace!("Unocked shared state in `restore_state_from_fullscreen`"); self.set_style_mask_async(mask); self.set_maximized(maximized); @@ -705,7 +758,7 @@ impl UnownedWindow { #[inline] pub fn fullscreen(&self) -> Option<Fullscreen> { - let shared_state_lock = self.shared_state.lock().unwrap(); + let shared_state_lock = self.lock_shared_state("fullscreen"); shared_state_lock.fullscreen.clone() } @@ -716,25 +769,20 @@ impl UnownedWindow { #[inline] pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) { - trace!("Locked shared state in `set_fullscreen`"); - let mut shared_state_lock = self.shared_state.lock().unwrap(); + let mut shared_state_lock = self.lock_shared_state("set_fullscreen"); if shared_state_lock.is_simple_fullscreen { - trace!("Unlocked shared state in `set_fullscreen`"); return; } if shared_state_lock.in_fullscreen_transition { // We can't set fullscreen here. // Set fullscreen after transition. shared_state_lock.target_fullscreen = Some(fullscreen); - trace!("Unlocked shared state in `set_fullscreen`"); return; } let old_fullscreen = shared_state_lock.fullscreen.clone(); if fullscreen == old_fullscreen { - trace!("Unlocked shared state in `set_fullscreen`"); return; } - trace!("Unlocked shared state in `set_fullscreen`"); drop(shared_state_lock); // If the fullscreen is on a different monitor, we must move the window @@ -787,8 +835,7 @@ impl UnownedWindow { if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) { unsafe { let app = NSApp(); - trace!("Locked shared state in `set_fullscreen`"); - let mut shared_state_lock = self.shared_state.lock().unwrap(); + let mut shared_state_lock = self.lock_shared_state("set_fullscreen"); shared_state_lock.save_presentation_opts = Some(app.presentationOptions_()); } } @@ -840,8 +887,7 @@ impl UnownedWindow { } } - trace!("Locked shared state in `set_fullscreen`"); - let mut shared_state_lock = self.shared_state.lock().unwrap(); + let mut shared_state_lock = self.lock_shared_state("set_fullscreen"); shared_state_lock.fullscreen = fullscreen.clone(); INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); @@ -884,7 +930,6 @@ impl UnownedWindow { // that the menu bar is disabled. This is done in the window // delegate in `window:willUseFullScreenPresentationOptions:`. let app = NSApp(); - trace!("Locked shared state in `set_fullscreen`"); shared_state_lock.save_presentation_opts = Some(app.presentationOptions_()); let presentation_options = @@ -918,7 +963,6 @@ impl UnownedWindow { }, _ => INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst), }; - trace!("Unlocked shared state in `set_fullscreen`"); } #[inline] @@ -927,9 +971,7 @@ impl UnownedWindow { self.decorations.store(decorations, Ordering::Release); let (fullscreen, resizable) = { - trace!("Locked shared state in `set_decorations`"); - let shared_state_lock = self.shared_state.lock().unwrap(); - trace!("Unlocked shared state in `set_decorations`"); + let shared_state_lock = self.lock_shared_state("set_decorations"); ( shared_state_lock.fullscreen.is_some(), shared_state_lock.resizable, diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index fc9be0fc..55ea8a07 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -265,14 +265,13 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i } extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL { - trace!("Triggered `windowShouldClose:`"); + trace_scope!("windowShouldClose:"); with_state(this, |state| state.emit_event(WindowEvent::CloseRequested)); - trace!("Completed `windowShouldClose:`"); NO } extern "C" fn window_will_close(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowWillClose:`"); + trace_scope!("windowWillClose:"); with_state(this, |state| unsafe { // `setDelegate:` retains the previous value and then autoreleases it autoreleasepool(|| { @@ -282,47 +281,42 @@ extern "C" fn window_will_close(this: &Object, _: Sel, _: id) { }); state.emit_event(WindowEvent::Destroyed); }); - trace!("Completed `windowWillClose:`"); } extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidResize:`"); + trace_scope!("windowDidResize:"); with_state(this, |state| { state.emit_resize_event(); state.emit_move_event(); }); - trace!("Completed `windowDidResize:`"); } // This won't be triggered if the move was part of a resize. extern "C" fn window_did_move(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidMove:`"); + trace_scope!("windowDidMove:"); with_state(this, |state| { state.emit_move_event(); }); - trace!("Completed `windowDidMove:`"); } extern "C" fn window_did_change_backing_properties(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidChangeBackingProperties:`"); + trace_scope!("windowDidChangeBackingProperties:"); with_state(this, |state| { state.emit_static_scale_factor_changed_event(); }); - trace!("Completed `windowDidChangeBackingProperties:`"); } extern "C" fn window_did_become_key(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidBecomeKey:`"); + trace_scope!("windowDidBecomeKey:"); with_state(this, |state| { // TODO: center the cursor if the window had mouse grab when it // lost focus state.emit_event(WindowEvent::Focused(true)); }); - trace!("Completed `windowDidBecomeKey:`"); } extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidResignKey:`"); + trace_scope!("windowDidResignKey:"); with_state(this, |state| { // It happens rather often, e.g. when the user is Cmd+Tabbing, that the // NSWindowDelegate will receive a didResignKey event despite no event @@ -349,12 +343,11 @@ extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) { state.emit_event(WindowEvent::Focused(false)); }); - trace!("Completed `windowDidResignKey:`"); } /// Invoked when the dragged image enters destination bounds or frame extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL { - trace!("Triggered `draggingEntered:`"); + trace_scope!("draggingEntered:"); use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration}; use std::path::PathBuf; @@ -376,20 +369,18 @@ extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL { } } - trace!("Completed `draggingEntered:`"); YES } /// Invoked when the image is released extern "C" fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL { - trace!("Triggered `prepareForDragOperation:`"); - trace!("Completed `prepareForDragOperation:`"); + trace_scope!("prepareForDragOperation:"); YES } /// Invoked after the released image has been removed from the screen extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL { - trace!("Triggered `performDragOperation:`"); + trace_scope!("performDragOperation:"); use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration}; use std::path::PathBuf; @@ -411,35 +402,31 @@ extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL } } - trace!("Completed `performDragOperation:`"); YES } /// Invoked when the dragging operation is complete extern "C" fn conclude_drag_operation(_: &Object, _: Sel, _: id) { - trace!("Triggered `concludeDragOperation:`"); - trace!("Completed `concludeDragOperation:`"); + trace_scope!("concludeDragOperation:"); } /// Invoked when the dragging operation is cancelled extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) { - trace!("Triggered `draggingExited:`"); + trace_scope!("draggingExited:"); with_state(this, |state| { state.emit_event(WindowEvent::HoveredFileCancelled) }); - trace!("Completed `draggingExited:`"); } /// Invoked when before enter fullscreen extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowWillEnterFullscreen:`"); + trace_scope!("windowWillEnterFullscreen:"); INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); with_state(this, |state| { state.with_window(|window| { - trace!("Locked shared state in `window_will_enter_fullscreen`"); - let mut shared_state = window.shared_state.lock().unwrap(); + let mut shared_state = window.lock_shared_state("window_will_enter_fullscreen"); shared_state.maximized = window.is_zoomed(); match shared_state.fullscreen { // Exclusive mode sets the state in `set_fullscreen` as the user @@ -458,27 +445,22 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { } } shared_state.in_fullscreen_transition = true; - trace!("Unlocked shared state in `window_will_enter_fullscreen`"); }) }); - trace!("Completed `windowWillEnterFullscreen:`"); } /// Invoked when before exit fullscreen extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowWillExitFullScreen:`"); + trace_scope!("windowWillExitFullScreen:"); INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); with_state(this, |state| { state.with_window(|window| { - trace!("Locked shared state in `window_will_exit_fullscreen`"); - let mut shared_state = window.shared_state.lock().unwrap(); + let mut shared_state = window.lock_shared_state("window_will_exit_fullscreen"); shared_state.in_fullscreen_transition = true; - trace!("Unlocked shared state in `window_will_exit_fullscreen`"); }); }); - trace!("Completed `windowWillExitFullScreen:`"); } extern "C" fn window_will_use_fullscreen_presentation_options( @@ -487,6 +469,7 @@ extern "C" fn window_will_use_fullscreen_presentation_options( _: id, proposed_options: NSUInteger, ) -> NSUInteger { + trace_scope!("window:willUseFullScreenPresentationOptions:"); // Generally, games will want to disable the menu bar and the dock. Ideally, // this would be configurable by the user. Unfortunately because of our // `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is @@ -498,15 +481,14 @@ extern "C" fn window_will_use_fullscreen_presentation_options( let mut options: NSUInteger = proposed_options; with_state(this, |state| { state.with_window(|window| { - trace!("Locked shared state in `window_will_use_fullscreen_presentation_options`"); - let shared_state = window.shared_state.lock().unwrap(); + let shared_state = + window.lock_shared_state("window_will_use_fullscreen_presentation_options"); if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen { options = (NSApplicationPresentationOptions::NSApplicationPresentationFullScreen | NSApplicationPresentationOptions::NSApplicationPresentationHideDock | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar) .bits(); } - trace!("Unlocked shared state in `window_will_use_fullscreen_presentation_options`"); }) }); @@ -515,46 +497,40 @@ extern "C" fn window_will_use_fullscreen_presentation_options( /// Invoked when entered fullscreen extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) { + trace_scope!("windowDidEnterFullscreen:"); INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst); - trace!("Triggered `windowDidEnterFullscreen:`"); with_state(this, |state| { state.initial_fullscreen = false; state.with_window(|window| { - trace!("Locked shared state in `window_did_enter_fullscreen`"); - let mut shared_state = window.shared_state.lock().unwrap(); + let mut shared_state = window.lock_shared_state("window_did_enter_fullscreen"); shared_state.in_fullscreen_transition = false; let target_fullscreen = shared_state.target_fullscreen.take(); - trace!("Unlocked shared state in `window_did_enter_fullscreen`"); drop(shared_state); if let Some(target_fullscreen) = target_fullscreen { window.set_fullscreen(target_fullscreen); } }); }); - trace!("Completed `windowDidEnterFullscreen:`"); } /// Invoked when exited fullscreen extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) { + trace_scope!("windowDidExitFullscreen:"); INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst); - trace!("Triggered `windowDidExitFullscreen:`"); with_state(this, |state| { state.with_window(|window| { window.restore_state_from_fullscreen(); - trace!("Locked shared state in `window_did_exit_fullscreen`"); - let mut shared_state = window.shared_state.lock().unwrap(); + let mut shared_state = window.lock_shared_state("window_did_exit_fullscreen"); shared_state.in_fullscreen_transition = false; let target_fullscreen = shared_state.target_fullscreen.take(); - trace!("Unlocked shared state in `window_did_exit_fullscreen`"); drop(shared_state); if let Some(target_fullscreen) = target_fullscreen { window.set_fullscreen(target_fullscreen); } }) }); - trace!("Completed `windowDidExitFullscreen:`"); } /// Invoked when fail to enter fullscreen @@ -574,14 +550,12 @@ extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) { /// This method indicates that there was an error, and you should clean up any /// work you may have done to prepare to enter full-screen mode. extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) { - trace!("Triggered `windowDidFailToEnterFullscreen:`"); + trace_scope!("windowDidFailToEnterFullscreen:"); with_state(this, |state| { state.with_window(|window| { - trace!("Locked shared state in `window_did_fail_to_enter_fullscreen`"); - let mut shared_state = window.shared_state.lock().unwrap(); + let mut shared_state = window.lock_shared_state("window_did_fail_to_enter_fullscreen"); shared_state.in_fullscreen_transition = false; shared_state.target_fullscreen = None; - trace!("Unlocked shared state in `window_did_fail_to_enter_fullscreen`"); }); if state.initial_fullscreen { let _: () = unsafe { @@ -595,5 +569,4 @@ extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) state.with_window(|window| window.restore_state_from_fullscreen()); } }); - trace!("Completed `windowDidFailToEnterFullscreen:`"); }