From cac0025abbbb90130a14bd8e601b47d371a5d31c Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 19 Nov 2014 10:09:29 +1000 Subject: Add support for setting a window delegate, and implement close event on mac. --- src/osx/mod.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 7 deletions(-) (limited to 'src/osx/mod.rs') diff --git a/src/osx/mod.rs b/src/osx/mod.rs index fb10a57..64cc529 100644 --- a/src/osx/mod.rs +++ b/src/osx/mod.rs @@ -1,7 +1,6 @@ use {CreationError, Event}; use CreationError::OsError; use libc; -use std::sync::atomic::AtomicBool; #[cfg(feature = "window")] use WindowBuilder; @@ -9,7 +8,9 @@ use WindowBuilder; #[cfg(feature = "headless")] use HeadlessRendererBuilder; -use cocoa::base::{id, NSUInteger, nil}; +use cocoa::base::{id, NSUInteger, nil, objc_allocateClassPair, class, objc_registerClassPair}; +use cocoa::base::{selector, msg_send, class_addMethod, class_addIvar}; +use cocoa::base::{object_setInstanceVariable, object_getInstanceVariable}; use cocoa::appkit; use cocoa::appkit::*; @@ -18,6 +19,9 @@ use core_foundation::string::CFString; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; use std::c_str::CString; +use std::mem; +use std::ptr; +use std::sync::atomic::{AtomicBool, Relaxed}; use events::Event::{MouseInput, MouseMoved, ReceivedCharacter, KeyboardInput}; use events::ElementState::{Pressed, Released}; @@ -34,11 +38,26 @@ static mut ctrl_pressed: bool = false; static mut win_pressed: bool = false; static mut alt_pressed: bool = false; +static DELEGATE_NAME: &'static [u8] = b"glutin_window_delegate\0"; +static DELEGATE_THIS_IVAR: &'static [u8] = b"glutin_this"; + +struct InternalState { + is_closed: AtomicBool, +} + +impl InternalState { + fn new() -> InternalState { + InternalState { + is_closed: AtomicBool::new(false), + } + } +} + pub struct Window { view: id, window: id, context: id, - is_closed: AtomicBool, + state: Box, } pub struct HeadlessContext(Window); @@ -64,6 +83,16 @@ impl HeadlessContext { } } +extern fn window_should_close(this: id, _: id) -> id { + unsafe { + let mut stored_value = ptr::null_mut(); + object_getInstanceVariable(this, DELEGATE_THIS_IVAR.as_ptr() as *const i8, &mut stored_value); + let state = stored_value as *mut InternalState; + (*state).is_closed.store(true, Relaxed); + } + 0 +} + impl Window { fn new_impl(dimensions: Option<(uint, uint)>, title: &str, monitor: Option, visible: bool) -> Result { let app = match Window::create_app() { @@ -93,9 +122,27 @@ impl Window { view: view, window: window, context: context, - is_closed: AtomicBool::new(false), + state: box InternalState::new(), }; + // Set up the window delegate to receive events + let ptr_size = mem::size_of::() as u64; + let ns_object = class("NSObject"); + + unsafe { + // Create a delegate class, add callback methods and store InternalState as user data. + let delegate = objc_allocateClassPair(ns_object, DELEGATE_NAME.as_ptr() as *const i8, 0); + class_addMethod(delegate, selector("windowShouldClose:"), window_should_close, "B@:@".to_c_str().as_ptr()); + class_addIvar(delegate, DELEGATE_THIS_IVAR.as_ptr() as *const i8, ptr_size, 3, "?".to_c_str().as_ptr()); + objc_registerClassPair(delegate); + + let del_obj = msg_send()(delegate, selector("alloc")); + let del_obj: id = msg_send()(del_obj, selector("init")); + object_setInstanceVariable(del_obj, DELEGATE_THIS_IVAR.as_ptr() as *const i8, + &*window.state as *const InternalState as *mut libc::c_void); + let _: id = msg_send()(window.window, selector("setDelegate:"), del_obj); + } + Ok(window) } @@ -127,7 +174,10 @@ impl Window { let masks = match monitor { Some(_) => NSBorderlessWindowMask as NSUInteger, - None => NSTitledWindowMask as NSUInteger | NSClosableWindowMask as NSUInteger | NSMiniaturizableWindowMask as NSUInteger, + None => NSTitledWindowMask as NSUInteger | + NSClosableWindowMask as NSUInteger | + NSMiniaturizableWindowMask as NSUInteger | + NSResizableWindowMask as NSUInteger, }; let window = NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( @@ -193,8 +243,7 @@ impl Window { } pub fn is_closed(&self) -> bool { - use std::sync::atomic::Relaxed; - self.is_closed.load(Relaxed) + self.state.is_closed.load(Relaxed) } pub fn set_title(&self, title: &str) { -- cgit v1.2.3