we (web engine): Experimental web browser project to understand the limits of Claude
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Review fix: extract shared event handlers to eliminate duplication

The 7 event handler callbacks (isFlipped, acceptsFirstResponder, keyDown:,
mouseDown:, mouseUp:, mouseMoved:, scrollWheel:) were duplicated verbatim
between WeView and WeMetalView class registrations (~120 lines). Extract
them to module-level extern "C" functions and a shared
register_view_event_handlers() helper.

Also rename MetalView._state to MetalView.state since the field is actively
used (not just kept alive for drop).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+133 -231
+133 -231
crates/platform/src/appkit.rs
··· 246 246 } 247 247 248 248 // --------------------------------------------------------------------------- 249 + // Shared NSView event handlers — used by both BitmapView and MetalView 250 + // --------------------------------------------------------------------------- 251 + 252 + /// `isFlipped` → YES (top-left origin). 253 + extern "C" fn view_is_flipped(_this: *mut c_void, _sel: *mut c_void) -> bool { 254 + true 255 + } 256 + 257 + /// `acceptsFirstResponder` → YES (allows view to receive key events). 258 + extern "C" fn view_accepts_first_responder(_this: *mut c_void, _sel: *mut c_void) -> bool { 259 + true 260 + } 261 + 262 + /// `keyDown:` — log key character and keyCode to stdout. 263 + extern "C" fn view_key_down(_this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 264 + let chars: *mut c_void = msg_send![event, characters]; 265 + if chars.is_null() { 266 + return; 267 + } 268 + let utf8: *const c_char = msg_send![chars, UTF8String]; 269 + if utf8.is_null() { 270 + return; 271 + } 272 + let c_str = unsafe { CStr::from_ptr(utf8) }; 273 + let key_code: u16 = msg_send![event, keyCode]; 274 + if let Ok(s) = c_str.to_str() { 275 + println!("keyDown: '{}' (keyCode: {})", s, key_code); 276 + } 277 + } 278 + 279 + /// `mouseDown:` — log mouse location to stdout. 280 + extern "C" fn view_mouse_down(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 281 + let raw_loc: NSPoint = msg_send![event, locationInWindow]; 282 + let loc: NSPoint = 283 + msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 284 + println!("mouseDown: ({:.1}, {:.1})", loc.x, loc.y); 285 + } 286 + 287 + /// `mouseUp:` — log mouse location to stdout. 288 + extern "C" fn view_mouse_up(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 289 + let raw_loc: NSPoint = msg_send![event, locationInWindow]; 290 + let loc: NSPoint = 291 + msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 292 + println!("mouseUp: ({:.1}, {:.1})", loc.x, loc.y); 293 + } 294 + 295 + /// `mouseMoved:` — log mouse location to stdout. 296 + extern "C" fn view_mouse_moved(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 297 + let raw_loc: NSPoint = msg_send![event, locationInWindow]; 298 + let loc: NSPoint = 299 + msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 300 + println!("mouseMoved: ({:.1}, {:.1})", loc.x, loc.y); 301 + } 302 + 303 + /// `scrollWheel:` — call scroll handler with delta and mouse location. 304 + extern "C" fn view_scroll_wheel(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 305 + let dx: f64 = msg_send![event, scrollingDeltaX]; 306 + let dy: f64 = msg_send![event, scrollingDeltaY]; 307 + let raw_loc: NSPoint = msg_send![event, locationInWindow]; 308 + let loc: NSPoint = 309 + msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 310 + // SAFETY: We are on the main thread (AppKit event loop). 311 + unsafe { 312 + if let Some(handler) = SCROLL_HANDLER { 313 + handler(dx, dy, loc.x, loc.y); 314 + } 315 + } 316 + } 317 + 318 + /// Register the shared event handlers on an NSView subclass being built. 319 + /// 320 + /// Adds: `isFlipped`, `acceptsFirstResponder`, `keyDown:`, `mouseDown:`, 321 + /// `mouseUp:`, `mouseMoved:`, `scrollWheel:`. 322 + fn register_view_event_handlers(view_class: &Class) { 323 + let sel = Sel::register(c"isFlipped"); 324 + view_class.add_method( 325 + sel, 326 + unsafe { std::mem::transmute::<*const (), Imp>(view_is_flipped as *const ()) }, 327 + c"B@:", 328 + ); 329 + 330 + let sel = Sel::register(c"acceptsFirstResponder"); 331 + view_class.add_method( 332 + sel, 333 + unsafe { std::mem::transmute::<*const (), Imp>(view_accepts_first_responder as *const ()) }, 334 + c"B@:", 335 + ); 336 + 337 + let sel = Sel::register(c"keyDown:"); 338 + view_class.add_method( 339 + sel, 340 + unsafe { std::mem::transmute::<*const (), Imp>(view_key_down as *const ()) }, 341 + c"v@:@", 342 + ); 343 + 344 + let sel = Sel::register(c"mouseDown:"); 345 + view_class.add_method( 346 + sel, 347 + unsafe { std::mem::transmute::<*const (), Imp>(view_mouse_down as *const ()) }, 348 + c"v@:@", 349 + ); 350 + 351 + let sel = Sel::register(c"mouseUp:"); 352 + view_class.add_method( 353 + sel, 354 + unsafe { std::mem::transmute::<*const (), Imp>(view_mouse_up as *const ()) }, 355 + c"v@:@", 356 + ); 357 + 358 + let sel = Sel::register(c"mouseMoved:"); 359 + view_class.add_method( 360 + sel, 361 + unsafe { std::mem::transmute::<*const (), Imp>(view_mouse_moved as *const ()) }, 362 + c"v@:@", 363 + ); 364 + 365 + let sel = Sel::register(c"scrollWheel:"); 366 + view_class.add_method( 367 + sel, 368 + unsafe { std::mem::transmute::<*const (), Imp>(view_scroll_wheel as *const ()) }, 369 + c"v@:@", 370 + ); 371 + } 372 + 373 + // --------------------------------------------------------------------------- 249 374 // BitmapView — custom NSView subclass for rendering a CG bitmap 250 375 // --------------------------------------------------------------------------- 251 376 ··· 327 452 c"v@:{CGRect={CGPoint=dd}{CGSize=dd}}", 328 453 ); 329 454 330 - // isFlipped -> YES (top-left origin) 331 - extern "C" fn is_flipped(_this: *mut c_void, _sel: *mut c_void) -> bool { 332 - true 333 - } 334 - 335 - let sel = Sel::register(c"isFlipped"); 336 - view_class.add_method( 337 - sel, 338 - unsafe { std::mem::transmute::<*const (), Imp>(is_flipped as *const ()) }, 339 - c"B@:", 340 - ); 341 - 342 - // acceptsFirstResponder -> YES (allows view to receive key events) 343 - extern "C" fn accepts_first_responder(_this: *mut c_void, _sel: *mut c_void) -> bool { 344 - true 345 - } 346 - 347 - let sel = Sel::register(c"acceptsFirstResponder"); 348 - view_class.add_method( 349 - sel, 350 - unsafe { std::mem::transmute::<*const (), Imp>(accepts_first_responder as *const ()) }, 351 - c"B@:", 352 - ); 353 - 354 - // keyDown: — log key character and keyCode to stdout 355 - extern "C" fn key_down(_this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 356 - let chars: *mut c_void = msg_send![event, characters]; 357 - if chars.is_null() { 358 - return; 359 - } 360 - let utf8: *const c_char = msg_send![chars, UTF8String]; 361 - if utf8.is_null() { 362 - return; 363 - } 364 - let c_str = unsafe { CStr::from_ptr(utf8) }; 365 - let key_code: u16 = msg_send![event, keyCode]; 366 - if let Ok(s) = c_str.to_str() { 367 - println!("keyDown: '{}' (keyCode: {})", s, key_code); 368 - } 369 - } 370 - 371 - let sel = Sel::register(c"keyDown:"); 372 - view_class.add_method( 373 - sel, 374 - unsafe { std::mem::transmute::<*const (), Imp>(key_down as *const ()) }, 375 - c"v@:@", 376 - ); 377 - 378 - // mouseDown: — log mouse location to stdout 379 - extern "C" fn mouse_down(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 380 - let raw_loc: NSPoint = msg_send![event, locationInWindow]; 381 - let loc: NSPoint = 382 - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 383 - println!("mouseDown: ({:.1}, {:.1})", loc.x, loc.y); 384 - } 385 - 386 - let sel = Sel::register(c"mouseDown:"); 387 - view_class.add_method( 388 - sel, 389 - unsafe { std::mem::transmute::<*const (), Imp>(mouse_down as *const ()) }, 390 - c"v@:@", 391 - ); 392 - 393 - // mouseUp: — log mouse location to stdout 394 - extern "C" fn mouse_up(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 395 - let raw_loc: NSPoint = msg_send![event, locationInWindow]; 396 - let loc: NSPoint = 397 - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 398 - println!("mouseUp: ({:.1}, {:.1})", loc.x, loc.y); 399 - } 400 - 401 - let sel = Sel::register(c"mouseUp:"); 402 - view_class.add_method( 403 - sel, 404 - unsafe { std::mem::transmute::<*const (), Imp>(mouse_up as *const ()) }, 405 - c"v@:@", 406 - ); 407 - 408 - // mouseMoved: — log mouse location to stdout 409 - extern "C" fn mouse_moved(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 410 - let raw_loc: NSPoint = msg_send![event, locationInWindow]; 411 - let loc: NSPoint = 412 - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 413 - println!("mouseMoved: ({:.1}, {:.1})", loc.x, loc.y); 414 - } 415 - 416 - let sel = Sel::register(c"mouseMoved:"); 417 - view_class.add_method( 418 - sel, 419 - unsafe { std::mem::transmute::<*const (), Imp>(mouse_moved as *const ()) }, 420 - c"v@:@", 421 - ); 422 - 423 - // scrollWheel: — call scroll handler with delta and mouse location 424 - extern "C" fn scroll_wheel(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 425 - let dx: f64 = msg_send![event, scrollingDeltaX]; 426 - let dy: f64 = msg_send![event, scrollingDeltaY]; 427 - let raw_loc: NSPoint = msg_send![event, locationInWindow]; 428 - let loc: NSPoint = 429 - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 430 - // SAFETY: We are on the main thread (AppKit event loop). 431 - unsafe { 432 - if let Some(handler) = SCROLL_HANDLER { 433 - handler(dx, dy, loc.x, loc.y); 434 - } 435 - } 436 - } 437 - 438 - let sel = Sel::register(c"scrollWheel:"); 439 - view_class.add_method( 440 - sel, 441 - unsafe { std::mem::transmute::<*const (), Imp>(scroll_wheel as *const ()) }, 442 - c"v@:@", 443 - ); 455 + // Shared event handlers: isFlipped, acceptsFirstResponder, key/mouse/scroll. 456 + register_view_event_handlers(&view_class); 444 457 445 458 view_class.register(); 446 459 } ··· 640 653 c"v@:", 641 654 ); 642 655 643 - // isFlipped -> YES (top-left origin) 644 - extern "C" fn is_flipped(_this: *mut c_void, _sel: *mut c_void) -> bool { 645 - true 646 - } 647 - 648 - let sel = Sel::register(c"isFlipped"); 649 - view_class.add_method( 650 - sel, 651 - unsafe { std::mem::transmute::<*const (), Imp>(is_flipped as *const ()) }, 652 - c"B@:", 653 - ); 654 - 655 - // acceptsFirstResponder -> YES 656 - extern "C" fn accepts_first_responder(_this: *mut c_void, _sel: *mut c_void) -> bool { 657 - true 658 - } 659 - 660 - let sel = Sel::register(c"acceptsFirstResponder"); 661 - view_class.add_method( 662 - sel, 663 - unsafe { std::mem::transmute::<*const (), Imp>(accepts_first_responder as *const ()) }, 664 - c"B@:", 665 - ); 666 - 667 - // keyDown: 668 - extern "C" fn key_down(_this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 669 - let chars: *mut c_void = msg_send![event, characters]; 670 - if chars.is_null() { 671 - return; 672 - } 673 - let utf8: *const c_char = msg_send![chars, UTF8String]; 674 - if utf8.is_null() { 675 - return; 676 - } 677 - let c_str = unsafe { CStr::from_ptr(utf8) }; 678 - let key_code: u16 = msg_send![event, keyCode]; 679 - if let Ok(s) = c_str.to_str() { 680 - println!("keyDown: '{}' (keyCode: {})", s, key_code); 681 - } 682 - } 683 - 684 - let sel = Sel::register(c"keyDown:"); 685 - view_class.add_method( 686 - sel, 687 - unsafe { std::mem::transmute::<*const (), Imp>(key_down as *const ()) }, 688 - c"v@:@", 689 - ); 690 - 691 - // mouseDown: 692 - extern "C" fn mouse_down(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 693 - let raw_loc: NSPoint = msg_send![event, locationInWindow]; 694 - let loc: NSPoint = 695 - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 696 - println!("mouseDown: ({:.1}, {:.1})", loc.x, loc.y); 697 - } 698 - 699 - let sel = Sel::register(c"mouseDown:"); 700 - view_class.add_method( 701 - sel, 702 - unsafe { std::mem::transmute::<*const (), Imp>(mouse_down as *const ()) }, 703 - c"v@:@", 704 - ); 705 - 706 - // mouseUp: 707 - extern "C" fn mouse_up(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 708 - let raw_loc: NSPoint = msg_send![event, locationInWindow]; 709 - let loc: NSPoint = 710 - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 711 - println!("mouseUp: ({:.1}, {:.1})", loc.x, loc.y); 712 - } 713 - 714 - let sel = Sel::register(c"mouseUp:"); 715 - view_class.add_method( 716 - sel, 717 - unsafe { std::mem::transmute::<*const (), Imp>(mouse_up as *const ()) }, 718 - c"v@:@", 719 - ); 720 - 721 - // mouseMoved: 722 - extern "C" fn mouse_moved(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 723 - let raw_loc: NSPoint = msg_send![event, locationInWindow]; 724 - let loc: NSPoint = 725 - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 726 - println!("mouseMoved: ({:.1}, {:.1})", loc.x, loc.y); 727 - } 728 - 729 - let sel = Sel::register(c"mouseMoved:"); 730 - view_class.add_method( 731 - sel, 732 - unsafe { std::mem::transmute::<*const (), Imp>(mouse_moved as *const ()) }, 733 - c"v@:@", 734 - ); 735 - 736 - // scrollWheel: 737 - extern "C" fn scroll_wheel(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { 738 - let dx: f64 = msg_send![event, scrollingDeltaX]; 739 - let dy: f64 = msg_send![event, scrollingDeltaY]; 740 - let raw_loc: NSPoint = msg_send![event, locationInWindow]; 741 - let loc: NSPoint = 742 - msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; 743 - unsafe { 744 - if let Some(handler) = SCROLL_HANDLER { 745 - handler(dx, dy, loc.x, loc.y); 746 - } 747 - } 748 - } 749 - 750 - let sel = Sel::register(c"scrollWheel:"); 751 - view_class.add_method( 752 - sel, 753 - unsafe { std::mem::transmute::<*const (), Imp>(scroll_wheel as *const ()) }, 754 - c"v@:@", 755 - ); 656 + // Shared event handlers: isFlipped, acceptsFirstResponder, key/mouse/scroll. 657 + register_view_event_handlers(&view_class); 756 658 757 659 view_class.register(); 758 660 } ··· 763 665 /// device, command queue, and layer are owned by the view's internal state. 764 666 pub struct MetalView { 765 667 view: Id, 766 - /// Boxed state kept alive for the view's ivar pointer. 767 - _state: Box<MetalViewState>, 668 + /// Boxed state — keeps Metal objects alive and provides `update_drawable_size`. 669 + state: Box<MetalViewState>, 768 670 } 769 671 770 672 impl MetalView { ··· 812 714 813 715 Some(MetalView { 814 716 view: view_id, 815 - _state: state, 717 + state, 816 718 }) 817 719 } 818 720 ··· 821 723 /// Call this when the window is resized. The width and height should be 822 724 /// in pixels (points × backing scale factor). 823 725 pub fn update_drawable_size(&self, width: f64, height: f64) { 824 - self._state.layer.set_drawable_size(width, height); 726 + self.state.layer.set_drawable_size(width, height); 825 727 } 826 728 827 729 /// Request the view to redraw (triggers `updateLayer`).