From cf77cfa2e03906ab8501d19ea1bb27c6457cb87a Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:20:47 +0200 Subject: [PATCH 1/6] X11 impl --- src/platform/x11/window.rs | 12 +++++++----- src/window.rs | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index 43245e2f..de97ef78 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -51,16 +51,18 @@ impl WindowHandle { }); } - pub fn close(&self) { + pub fn is_open(&self) -> bool { + self.is_open.load(Ordering::Relaxed) + } +} + +impl Drop for WindowHandle { + fn drop(&mut self) { self.close_requested.store(true, Ordering::Relaxed); if let Some(event_loop) = self.event_loop_handle.take() { let _ = event_loop.join(); } } - - pub fn is_open(&self) -> bool { - self.is_open.load(Ordering::Relaxed) - } } pub(crate) struct ParentHandle { diff --git a/src/window.rs b/src/window.rs index 240092d8..fc44bdc3 100644 --- a/src/window.rs +++ b/src/window.rs @@ -20,8 +20,8 @@ impl WindowHandle { } /// Close the window - pub fn close(&self) { - self.window_handle.close(); + pub fn close(self) { + drop(self) } /// Returns `true` if the window is still open, and returns `false` From b609b95c91c9fe2667e1c4bdad9e5e5644964f4e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:24:56 +0200 Subject: [PATCH 2/6] Win32 impl --- src/platform/win/window.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index dbfc32e3..ef1ece5e 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -4,11 +4,11 @@ use windows_sys::Win32::{ UI::{ Controls::WM_MOUSELEAVE, WindowsAndMessaging::{ - PostMessageW, HTCLIENT, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_DPICHANGED, - WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, - WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, - WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SIZE, WM_SYSCHAR, - WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, + HTCLIENT, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_DPICHANGED, WM_INPUTLANGCHANGE, + WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, + WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN, + WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, + WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, }, }, }; @@ -50,7 +50,7 @@ const WIN_FRAME_TIMER: NonZeroUsize = match NonZeroUsize::new(4242) { }; pub struct WindowHandle { - hwnd: Cell>, + hwnd: Cell>, is_open: Rc>, } @@ -59,19 +59,19 @@ impl WindowHandle { run_thread_message_loop_until(|| !self.is_open()).unwrap(); } - pub fn close(&self) { - if let Some(hwnd) = self.hwnd.take() { - unsafe { - PostMessageW(hwnd, BV_WINDOW_MUST_CLOSE, 0, 0); - } - } - } - pub fn is_open(&self) -> bool { self.is_open.get() } } +impl Drop for WindowHandle { + fn drop(&mut self) { + if let Some(hwnd) = self.hwnd.take() { + let _ = hwnd.destroy(); + } + } +} + struct ParentHandle { is_open: Rc>, } @@ -474,7 +474,7 @@ impl WindowHandle { window.show_and_activate(); - WindowHandle { hwnd: Some(hwnd).into(), is_open: Rc::clone(&is_open) } + WindowHandle { hwnd: Some(window).into(), is_open: Rc::clone(&is_open) } } } From 10ad8c0b7c0a7c68baac7894e7fa542a83b96423 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:44:05 +0200 Subject: [PATCH 3/6] macOS impl --- src/platform/macos/context.rs | 7 +- src/platform/macos/view.rs | 8 ++- src/platform/macos/window.rs | 10 +++ src/wrappers/appkit/view.rs | 19 ++++-- src/wrappers/appkit/view/implementation.rs | 78 ++++++++++++++-------- 5 files changed, 84 insertions(+), 38 deletions(-) diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs index 2961a4df..a8cfe27a 100644 --- a/src/platform/macos/context.rs +++ b/src/platform/macos/context.rs @@ -29,7 +29,8 @@ impl WindowContext { pub fn request_close(&self) { let Some(view) = self.view.load() else { return }; - BaseviewView::close(view.inner_ref()); + let Some(view) = view.inner_ref() else { return }; + BaseviewView::close(view); } pub fn has_focus(&self) -> bool { @@ -58,7 +59,7 @@ impl WindowContext { pub fn resize(&self, size: Size) { let Some(view) = self.view.load() else { return }; - let view = view.inner_ref(); + let Some(view) = view.inner_ref() else { return }; if view.inner.state.closed.get() { return; } @@ -82,7 +83,7 @@ impl WindowContext { #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option { - Some(crate::gl::GlContext::new(self.view.load()?.inner().gl_context.get()?.clone())) + Some(crate::gl::GlContext::new(self.view.load()?.inner()?.gl_context.get()?.clone())) } pub fn window_handle(&self) -> Option> { diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index e5cd28d6..f14587f3 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -106,14 +106,18 @@ impl BaseviewView { let timer_view = Weak::new(view.view); view.frame_timer.set(TimerHandle::new(0.015, move || { if let Some(view) = timer_view.load() { - Self::trigger_frame(view.inner_ref()); + if let Some(view) = view.inner_ref() { + Self::trigger_frame(view); + } } })); let notifier_view = Weak::new(view.view); let observer = NotificationCenterObserver::register_window_key_change(move |n| { if let Some(view) = notifier_view.load() { - BaseviewView::handle_notification(view.inner_ref(), n); + if let Some(view) = view.inner_ref() { + BaseviewView::handle_notification(view, n); + } } }); view.notification_center_observer.set(Some(observer)); diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 51cf91b1..9fd17261 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -18,6 +18,16 @@ pub struct WindowHandle { state: Rc, } +impl Drop for WindowHandle { + fn drop(&mut self) { + let Some(view) = self.view.take() else { return }; + let Some(view) = view.load() else { return }; + let Some(view) = view.inner_ref() else { return }; + + BaseviewView::close(view); + } +} + impl WindowHandle { pub fn create_window(mut options: WindowOpenOptions, handler: WindowHandlerBuilder) -> Self { autoreleasepool(|_| { diff --git a/src/wrappers/appkit/view.rs b/src/wrappers/appkit/view.rs index c5a27e5b..4394b51e 100644 --- a/src/wrappers/appkit/view.rs +++ b/src/wrappers/appkit/view.rs @@ -51,7 +51,7 @@ impl View { let view: Retained> = unsafe { msg_send![view, initWithFrame: frame] }; - init(view.inner_ref()); + init(view.inner_ref().unwrap()); view } @@ -68,23 +68,28 @@ impl View { let ivar = class.instance_variable(BASEVIEW_STATE_IVAR).unwrap(); let ivar = unsafe { ivar.load_ptr::<*mut c_void>(this) }; let raw = unsafe { ivar.read() }; + + if raw.is_null() { + return; + } + let inner = unsafe { Box::>::from_raw(raw.cast()) }; unsafe { ivar.write(core::ptr::null_mut()) }; drop(inner); } - fn get_inner(&self) -> &ViewInner { + fn get_inner(&self) -> Option<&ViewInner> { let ivar = self.class().instance_variable(BASEVIEW_STATE_IVAR).unwrap(); let ivar = unsafe { ivar.load::<*mut c_void>(self) }; - unsafe { ivar.cast::>().as_ref() }.unwrap() + unsafe { ivar.cast::>().as_ref() } } - pub fn inner(&self) -> &V { - &self.get_inner().inner + pub fn inner(&self) -> Option<&V> { + Some(&self.get_inner()?.inner) } - pub fn inner_ref(&self) -> ViewRef<'_, V> { - ViewRef { view: self, inner: self.inner() } + pub fn inner_ref(&self) -> Option> { + Some(ViewRef { view: self, inner: self.inner()? }) } pub fn window_handle_from_weak(this: &Weak) -> Option> { diff --git a/src/wrappers/appkit/view/implementation.rs b/src/wrappers/appkit/view/implementation.rs index a3706768..af5c8f21 100644 --- a/src/wrappers/appkit/view/implementation.rs +++ b/src/wrappers/appkit/view/implementation.rs @@ -162,125 +162,151 @@ extern "C-unwind" fn accepts_first_mouse(_this: &NSView, _sel: Sel, _event: &NSE } extern "C-unwind" fn become_first_responder(this: &View, _sel: Sel) -> Bool { - V::become_first_responder(this.inner_ref()).into() + let Some(inner) = this.inner_ref() else { return false.into() }; + V::become_first_responder(inner).into() } extern "C-unwind" fn resign_first_responder(this: &View, _sel: Sel) -> Bool { - V::resign_first_responder(this.inner_ref()).into() + let Some(inner) = this.inner_ref() else { return true.into() }; + V::resign_first_responder(inner).into() } extern "C-unwind" fn window_should_close( this: &View, _: Sel, _sender: &AnyObject, ) -> Bool { - V::window_should_close(this.inner_ref()).into() + let Some(inner) = this.inner_ref() else { return true.into() }; + V::window_should_close(inner).into() } extern "C-unwind" fn view_did_change_backing_properties( this: &View, _: Sel, _: &AnyObject, ) { - V::view_did_change_backing_properties(this.inner_ref()); + let Some(inner) = this.inner_ref() else { return }; + V::view_did_change_backing_properties(inner); } extern "C-unwind" fn hit_test( this: &View, _sel: Sel, point: NSPoint, ) -> Option<&NSView> { - V::hit_test(this.inner_ref(), point) + let Some(inner) = this.inner_ref() else { return None }; + V::hit_test(inner, point) } extern "C-unwind" fn view_will_move_to_window( this: &View, _self: Sel, new_window: Option<&NSWindow>, ) { - V::view_will_move_to_window(this.inner_ref(), new_window); + let Some(inner) = this.inner_ref() else { return }; + V::view_will_move_to_window(inner, new_window); } extern "C-unwind" fn update_tracking_areas(this: &View, _self: Sel, _: &AnyObject) { - V::update_tracking_areas(this.inner_ref()); + let Some(inner) = this.inner_ref() else { return }; + V::update_tracking_areas(inner); } extern "C-unwind" fn mouse_moved(this: &View, _sel: Sel, event: &NSEvent) { - V::mouse_moved(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::mouse_moved(inner, event); } extern "C-unwind" fn scroll_wheel(this: &View, _: Sel, event: &NSEvent) { - V::scroll_wheel(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::scroll_wheel(inner, event); } extern "C-unwind" fn dragging_entered( this: &View, _sel: Sel, sender: Option<&ProtocolObject>, ) -> NSDragOperation { - V::dragging_entered(this.inner_ref(), sender) + let Some(inner) = this.inner_ref() else { return NSDragOperation::None }; + V::dragging_entered(inner, sender) } extern "C-unwind" fn dragging_updated( this: &View, _sel: Sel, sender: Option<&ProtocolObject>, ) -> NSDragOperation { - V::dragging_updated(this.inner_ref(), sender) + let Some(inner) = this.inner_ref() else { return NSDragOperation::None }; + V::dragging_updated(inner, sender) } extern "C-unwind" fn prepare_for_drag_operation( this: &View, _sel: Sel, sender: Option<&ProtocolObject>, ) -> Bool { - V::prepare_for_drag_operation(this.inner_ref(), sender).into() + let Some(inner) = this.inner_ref() else { return false.into() }; + V::prepare_for_drag_operation(inner, sender).into() } extern "C-unwind" fn perform_drag_operation( this: &View, _sel: Sel, sender: Option<&ProtocolObject>, ) -> Bool { - V::perform_drag_operation(this.inner_ref(), sender).into() + let Some(inner) = this.inner_ref() else { return false.into() }; + V::perform_drag_operation(inner, sender).into() } extern "C-unwind" fn dragging_exited( this: &View, _sel: Sel, sender: Option<&ProtocolObject>, ) { - V::dragging_exited(this.inner_ref(), sender) + let Some(inner) = this.inner_ref() else { return }; + V::dragging_exited(inner, sender) } extern "C-unwind" fn handle_notification( this: &View, _cmd: Sel, notification: &NSNotification, ) { - V::handle_notification(this.inner_ref(), notification) + let Some(inner) = this.inner_ref() else { return }; + V::handle_notification(inner, notification) } extern "C-unwind" fn mouse_entered(this: &View, _: Sel, _: &AnyObject) { - V::mouse_entered(this.inner_ref()); + let Some(inner) = this.inner_ref() else { return }; + V::mouse_entered(inner); } extern "C-unwind" fn mouse_exited(this: &View, _: Sel, _: &AnyObject) { - V::mouse_exited(this.inner_ref()); + let Some(inner) = this.inner_ref() else { return }; + V::mouse_exited(inner); } extern "C-unwind" fn key_down(this: &View, _: Sel, event: &NSEvent) { - V::key_down(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::key_down(inner, event); } extern "C-unwind" fn key_up(this: &View, _: Sel, event: &NSEvent) { - V::key_up(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::key_up(inner, event); } extern "C-unwind" fn flags_changed(this: &View, _: Sel, event: &NSEvent) { - V::flags_changed(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::flags_changed(inner, event); } extern "C-unwind" fn mouse_down(this: &View, _sel: Sel, event: &NSEvent) { - V::mouse_down(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::mouse_down(inner, event); } extern "C-unwind" fn mouse_up(this: &View, _sel: Sel, event: &NSEvent) { - V::mouse_up(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::mouse_up(inner, event); } extern "C-unwind" fn right_mouse_down(this: &View, _sel: Sel, event: &NSEvent) { - V::right_mouse_down(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::right_mouse_down(inner, event); } extern "C-unwind" fn right_mouse_up(this: &View, _sel: Sel, event: &NSEvent) { - V::right_mouse_up(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::right_mouse_up(inner, event); } extern "C-unwind" fn other_mouse_down(this: &View, _sel: Sel, event: &NSEvent) { - V::other_mouse_down(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::other_mouse_down(inner, event); } extern "C-unwind" fn other_mouse_up(this: &View, _sel: Sel, event: &NSEvent) { - V::other_mouse_up(this.inner_ref(), event); + let Some(inner) = this.inner_ref() else { return }; + V::other_mouse_up(inner, event); } From 2a3b71a8f22a7af0c0d27f932557b15e9571a56e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sat, 11 Jul 2026 18:48:30 +0200 Subject: [PATCH 4/6] Clippy fix --- src/wrappers/appkit/view/implementation.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wrappers/appkit/view/implementation.rs b/src/wrappers/appkit/view/implementation.rs index af5c8f21..96026124 100644 --- a/src/wrappers/appkit/view/implementation.rs +++ b/src/wrappers/appkit/view/implementation.rs @@ -188,8 +188,7 @@ extern "C-unwind" fn view_did_change_backing_properties( extern "C-unwind" fn hit_test( this: &View, _sel: Sel, point: NSPoint, ) -> Option<&NSView> { - let Some(inner) = this.inner_ref() else { return None }; - V::hit_test(inner, point) + V::hit_test(this.inner_ref()?, point) } extern "C-unwind" fn view_will_move_to_window( From 4004444e40ddc5f9e26fa054a9f17f9ede7a1424 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 12 Jul 2026 01:45:10 +0200 Subject: [PATCH 5/6] fixes --- src/platform/macos/window.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 9fd17261..6d343005 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -20,8 +20,7 @@ pub struct WindowHandle { impl Drop for WindowHandle { fn drop(&mut self) { - let Some(view) = self.view.take() else { return }; - let Some(view) = view.load() else { return }; + let Some(view) = self.view.load() else { return }; let Some(view) = view.inner_ref() else { return }; BaseviewView::close(view); @@ -85,14 +84,6 @@ impl WindowHandle { NSApplication::sharedApplication(self.mtm).run(); } - pub fn close(&self) { - let Some(view) = self.view.load() else { - return; - }; - - BaseviewView::close(view.inner_ref()); - } - pub fn is_open(&self) -> bool { self.state.closed.get() } From b7e6dabcc87299c5c4c9bbf6d995afcb1f6e2eb1 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Sun, 12 Jul 2026 01:54:45 +0200 Subject: [PATCH 6/6] fixes --- src/platform/win/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index ef1ece5e..83749fbe 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -1,6 +1,6 @@ use windows_core::{ComObject, Result, HSTRING}; use windows_sys::Win32::{ - Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM}, + Foundation::{LPARAM, LRESULT, RECT, WPARAM}, UI::{ Controls::WM_MOUSELEAVE, WindowsAndMessaging::{