diff --git a/crates/platform/src/appkit.rs b/crates/platform/src/appkit.rs index e0b2b0b..5cf0e82 100644 --- a/crates/platform/src/appkit.rs +++ b/crates/platform/src/appkit.rs @@ -245,6 +245,131 @@ impl Window { } } +// --------------------------------------------------------------------------- +// Shared NSView event handlers — used by both BitmapView and MetalView +// --------------------------------------------------------------------------- + +/// `isFlipped` → YES (top-left origin). +extern "C" fn view_is_flipped(_this: *mut c_void, _sel: *mut c_void) -> bool { + true +} + +/// `acceptsFirstResponder` → YES (allows view to receive key events). +extern "C" fn view_accepts_first_responder(_this: *mut c_void, _sel: *mut c_void) -> bool { + true +} + +/// `keyDown:` — log key character and keyCode to stdout. +extern "C" fn view_key_down(_this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { + let chars: *mut c_void = msg_send![event, characters]; + if chars.is_null() { + return; + } + let utf8: *const c_char = msg_send![chars, UTF8String]; + if utf8.is_null() { + return; + } + let c_str = unsafe { CStr::from_ptr(utf8) }; + let key_code: u16 = msg_send![event, keyCode]; + if let Ok(s) = c_str.to_str() { + println!("keyDown: '{}' (keyCode: {})", s, key_code); + } +} + +/// `mouseDown:` — log mouse location to stdout. +extern "C" fn view_mouse_down(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { + let raw_loc: NSPoint = msg_send![event, locationInWindow]; + let loc: NSPoint = + msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; + println!("mouseDown: ({:.1}, {:.1})", loc.x, loc.y); +} + +/// `mouseUp:` — log mouse location to stdout. +extern "C" fn view_mouse_up(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { + let raw_loc: NSPoint = msg_send![event, locationInWindow]; + let loc: NSPoint = + msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; + println!("mouseUp: ({:.1}, {:.1})", loc.x, loc.y); +} + +/// `mouseMoved:` — log mouse location to stdout. +extern "C" fn view_mouse_moved(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { + let raw_loc: NSPoint = msg_send![event, locationInWindow]; + let loc: NSPoint = + msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; + println!("mouseMoved: ({:.1}, {:.1})", loc.x, loc.y); +} + +/// `scrollWheel:` — call scroll handler with delta and mouse location. +extern "C" fn view_scroll_wheel(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { + let dx: f64 = msg_send![event, scrollingDeltaX]; + let dy: f64 = msg_send![event, scrollingDeltaY]; + let raw_loc: NSPoint = msg_send![event, locationInWindow]; + let loc: NSPoint = + msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; + // SAFETY: We are on the main thread (AppKit event loop). + unsafe { + if let Some(handler) = SCROLL_HANDLER { + handler(dx, dy, loc.x, loc.y); + } + } +} + +/// Register the shared event handlers on an NSView subclass being built. +/// +/// Adds: `isFlipped`, `acceptsFirstResponder`, `keyDown:`, `mouseDown:`, +/// `mouseUp:`, `mouseMoved:`, `scrollWheel:`. +fn register_view_event_handlers(view_class: &Class) { + let sel = Sel::register(c"isFlipped"); + view_class.add_method( + sel, + unsafe { std::mem::transmute::<*const (), Imp>(view_is_flipped as *const ()) }, + c"B@:", + ); + + let sel = Sel::register(c"acceptsFirstResponder"); + view_class.add_method( + sel, + unsafe { std::mem::transmute::<*const (), Imp>(view_accepts_first_responder as *const ()) }, + c"B@:", + ); + + let sel = Sel::register(c"keyDown:"); + view_class.add_method( + sel, + unsafe { std::mem::transmute::<*const (), Imp>(view_key_down as *const ()) }, + c"v@:@", + ); + + let sel = Sel::register(c"mouseDown:"); + view_class.add_method( + sel, + unsafe { std::mem::transmute::<*const (), Imp>(view_mouse_down as *const ()) }, + c"v@:@", + ); + + let sel = Sel::register(c"mouseUp:"); + view_class.add_method( + sel, + unsafe { std::mem::transmute::<*const (), Imp>(view_mouse_up as *const ()) }, + c"v@:@", + ); + + let sel = Sel::register(c"mouseMoved:"); + view_class.add_method( + sel, + unsafe { std::mem::transmute::<*const (), Imp>(view_mouse_moved as *const ()) }, + c"v@:@", + ); + + let sel = Sel::register(c"scrollWheel:"); + view_class.add_method( + sel, + unsafe { std::mem::transmute::<*const (), Imp>(view_scroll_wheel as *const ()) }, + c"v@:@", + ); +} + // --------------------------------------------------------------------------- // BitmapView — custom NSView subclass for rendering a CG bitmap // --------------------------------------------------------------------------- @@ -327,120 +452,8 @@ fn register_we_view_class() { c"v@:{CGRect={CGPoint=dd}{CGSize=dd}}", ); - // isFlipped -> YES (top-left origin) - extern "C" fn is_flipped(_this: *mut c_void, _sel: *mut c_void) -> bool { - true - } - - let sel = Sel::register(c"isFlipped"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(is_flipped as *const ()) }, - c"B@:", - ); - - // acceptsFirstResponder -> YES (allows view to receive key events) - extern "C" fn accepts_first_responder(_this: *mut c_void, _sel: *mut c_void) -> bool { - true - } - - let sel = Sel::register(c"acceptsFirstResponder"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(accepts_first_responder as *const ()) }, - c"B@:", - ); - - // keyDown: — log key character and keyCode to stdout - extern "C" fn key_down(_this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let chars: *mut c_void = msg_send![event, characters]; - if chars.is_null() { - return; - } - let utf8: *const c_char = msg_send![chars, UTF8String]; - if utf8.is_null() { - return; - } - let c_str = unsafe { CStr::from_ptr(utf8) }; - let key_code: u16 = msg_send![event, keyCode]; - if let Ok(s) = c_str.to_str() { - println!("keyDown: '{}' (keyCode: {})", s, key_code); - } - } - - let sel = Sel::register(c"keyDown:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(key_down as *const ()) }, - c"v@:@", - ); - - // mouseDown: — log mouse location to stdout - extern "C" fn mouse_down(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let raw_loc: NSPoint = msg_send![event, locationInWindow]; - let loc: NSPoint = - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; - println!("mouseDown: ({:.1}, {:.1})", loc.x, loc.y); - } - - let sel = Sel::register(c"mouseDown:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(mouse_down as *const ()) }, - c"v@:@", - ); - - // mouseUp: — log mouse location to stdout - extern "C" fn mouse_up(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let raw_loc: NSPoint = msg_send![event, locationInWindow]; - let loc: NSPoint = - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; - println!("mouseUp: ({:.1}, {:.1})", loc.x, loc.y); - } - - let sel = Sel::register(c"mouseUp:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(mouse_up as *const ()) }, - c"v@:@", - ); - - // mouseMoved: — log mouse location to stdout - extern "C" fn mouse_moved(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let raw_loc: NSPoint = msg_send![event, locationInWindow]; - let loc: NSPoint = - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; - println!("mouseMoved: ({:.1}, {:.1})", loc.x, loc.y); - } - - let sel = Sel::register(c"mouseMoved:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(mouse_moved as *const ()) }, - c"v@:@", - ); - - // scrollWheel: — call scroll handler with delta and mouse location - extern "C" fn scroll_wheel(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let dx: f64 = msg_send![event, scrollingDeltaX]; - let dy: f64 = msg_send![event, scrollingDeltaY]; - let raw_loc: NSPoint = msg_send![event, locationInWindow]; - let loc: NSPoint = - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; - // SAFETY: We are on the main thread (AppKit event loop). - unsafe { - if let Some(handler) = SCROLL_HANDLER { - handler(dx, dy, loc.x, loc.y); - } - } - } - - let sel = Sel::register(c"scrollWheel:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(scroll_wheel as *const ()) }, - c"v@:@", - ); + // Shared event handlers: isFlipped, acceptsFirstResponder, key/mouse/scroll. + register_view_event_handlers(&view_class); view_class.register(); } @@ -640,119 +653,8 @@ fn register_we_metal_view_class() { c"v@:", ); - // isFlipped -> YES (top-left origin) - extern "C" fn is_flipped(_this: *mut c_void, _sel: *mut c_void) -> bool { - true - } - - let sel = Sel::register(c"isFlipped"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(is_flipped as *const ()) }, - c"B@:", - ); - - // acceptsFirstResponder -> YES - extern "C" fn accepts_first_responder(_this: *mut c_void, _sel: *mut c_void) -> bool { - true - } - - let sel = Sel::register(c"acceptsFirstResponder"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(accepts_first_responder as *const ()) }, - c"B@:", - ); - - // keyDown: - extern "C" fn key_down(_this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let chars: *mut c_void = msg_send![event, characters]; - if chars.is_null() { - return; - } - let utf8: *const c_char = msg_send![chars, UTF8String]; - if utf8.is_null() { - return; - } - let c_str = unsafe { CStr::from_ptr(utf8) }; - let key_code: u16 = msg_send![event, keyCode]; - if let Ok(s) = c_str.to_str() { - println!("keyDown: '{}' (keyCode: {})", s, key_code); - } - } - - let sel = Sel::register(c"keyDown:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(key_down as *const ()) }, - c"v@:@", - ); - - // mouseDown: - extern "C" fn mouse_down(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let raw_loc: NSPoint = msg_send![event, locationInWindow]; - let loc: NSPoint = - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; - println!("mouseDown: ({:.1}, {:.1})", loc.x, loc.y); - } - - let sel = Sel::register(c"mouseDown:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(mouse_down as *const ()) }, - c"v@:@", - ); - - // mouseUp: - extern "C" fn mouse_up(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let raw_loc: NSPoint = msg_send![event, locationInWindow]; - let loc: NSPoint = - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; - println!("mouseUp: ({:.1}, {:.1})", loc.x, loc.y); - } - - let sel = Sel::register(c"mouseUp:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(mouse_up as *const ()) }, - c"v@:@", - ); - - // mouseMoved: - extern "C" fn mouse_moved(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let raw_loc: NSPoint = msg_send![event, locationInWindow]; - let loc: NSPoint = - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; - println!("mouseMoved: ({:.1}, {:.1})", loc.x, loc.y); - } - - let sel = Sel::register(c"mouseMoved:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(mouse_moved as *const ()) }, - c"v@:@", - ); - - // scrollWheel: - extern "C" fn scroll_wheel(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { - let dx: f64 = msg_send![event, scrollingDeltaX]; - let dy: f64 = msg_send![event, scrollingDeltaY]; - let raw_loc: NSPoint = msg_send![event, locationInWindow]; - let loc: NSPoint = - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::()]; - unsafe { - if let Some(handler) = SCROLL_HANDLER { - handler(dx, dy, loc.x, loc.y); - } - } - } - - let sel = Sel::register(c"scrollWheel:"); - view_class.add_method( - sel, - unsafe { std::mem::transmute::<*const (), Imp>(scroll_wheel as *const ()) }, - c"v@:@", - ); + // Shared event handlers: isFlipped, acceptsFirstResponder, key/mouse/scroll. + register_view_event_handlers(&view_class); view_class.register(); } @@ -763,8 +665,8 @@ fn register_we_metal_view_class() { /// device, command queue, and layer are owned by the view's internal state. pub struct MetalView { view: Id, - /// Boxed state kept alive for the view's ivar pointer. - _state: Box, + /// Boxed state — keeps Metal objects alive and provides `update_drawable_size`. + state: Box, } impl MetalView { @@ -812,7 +714,7 @@ impl MetalView { Some(MetalView { view: view_id, - _state: state, + state, }) } @@ -821,7 +723,7 @@ impl MetalView { /// Call this when the window is resized. The width and height should be /// in pixels (points × backing scale factor). pub fn update_drawable_size(&self, width: f64, height: f64) { - self._state.layer.set_drawable_size(width, height); + self.state.layer.set_drawable_size(width, height); } /// Request the view to redraw (triggers `updateLayer`).