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:`");
 }