diff --git a/src/android/mod.rs b/src/android/mod.rs index 8bc82ceb..ad9d00ac 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -273,6 +273,9 @@ impl Window { pub fn get_api(&self) -> ::Api { ::Api::OpenGlEs } + + pub fn set_window_resize_callback(&mut self, _: fn(uint, uint)) { + } } #[cfg(feature = "window")] diff --git a/src/lib.rs b/src/lib.rs index 029b5769..48fcc85f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -482,6 +482,13 @@ impl Window { proxy: self.window.create_window_proxy() } } + + /// Sets a resize callback that is called by Mac (and potentially other + /// operating systems) during resize operations. This can be used to repaint + /// during window resizing. + pub fn set_window_resize_callback(&mut self, callback: fn(uint, uint)) { + self.window.set_window_resize_callback(callback); + } } #[cfg(feature = "window")] @@ -542,6 +549,9 @@ impl HeadlessContext { pub fn get_api(&self) -> Api { self.context.get_api() } + + pub fn set_window_resize_callback(&mut self, _: fn(uint, uint)) { + } } #[cfg(feature = "headless")] diff --git a/src/osx/mod.rs b/src/osx/mod.rs index 42a9ea24..2634491e 100644 --- a/src/osx/mod.rs +++ b/src/osx/mod.rs @@ -18,12 +18,12 @@ use core_foundation::base::TCFType; use core_foundation::string::CFString; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; +use std::cell::Cell; use std::c_str::CString; use std::mem; use std::ptr; -use std::sync::atomic::{AtomicBool, Relaxed}; -use events::Event::{MouseInput, MouseMoved, ReceivedCharacter, KeyboardInput, MouseWheel}; +use events::Event::{MouseInput, MouseMoved, ReceivedCharacter, KeyboardInput, Resized, MouseWheel}; use events::ElementState::{Pressed, Released}; use events::MouseButton::{LeftMouseButton, RightMouseButton}; use events; @@ -42,25 +42,24 @@ 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"; +static DELEGATE_STATE_IVAR: &'static [u8] = b"glutin_state"; -struct InternalState { - is_closed: AtomicBool, -} - -impl InternalState { - fn new() -> InternalState { - InternalState { - is_closed: AtomicBool::new(false), - } - } +struct DelegateState<'a> { + is_closed: bool, + context: id, + view: id, + window: &'a Window, + handler: fn(uint, uint), } pub struct Window { view: id, window: id, context: id, - state: Box, + delegate: id, + resize: fn(uint, uint), + + is_closed: Cell, } #[cfg(feature = "window")] @@ -103,9 +102,24 @@ impl WindowProxy { 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); + object_getInstanceVariable(this, DELEGATE_STATE_IVAR.as_ptr() as *const i8, &mut stored_value); + let state = stored_value as *mut DelegateState; + + (*state).is_closed = true; + } + 0 +} + +extern fn window_did_resize(this: id, _: id) -> id { + unsafe { + let mut stored_value = ptr::null_mut(); + object_getInstanceVariable(this, DELEGATE_STATE_IVAR.as_ptr() as *const i8, &mut stored_value); + let state = &mut *(stored_value as *mut DelegateState); + + let _: id = msg_send()(state.context, selector("update")); + + let rect = NSView::frame(state.view); + (state.handler)(rect.size.width as uint, rect.size.height as uint); } 0 } @@ -136,34 +150,41 @@ impl Window { window.makeKeyAndOrderFront_(nil); } - let window = Window { - view: view, - window: window, - context: context, - 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 { + let delegate = 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()); + class_addMethod(delegate, selector("windowDidResize:"), window_did_resize, "V@:@".to_c_str().as_ptr()); + class_addIvar(delegate, DELEGATE_STATE_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); - } + let _: id = msg_send()(window, selector("setDelegate:"), del_obj); + del_obj + }; + + let window = Window { + view: view, + window: window, + context: context, + delegate: delegate, + resize: Window::resize, + + is_closed: Cell::new(false), + }; Ok(window) } + fn resize(_: uint, _: uint) { + + } + fn create_app() -> Option { unsafe { let app = NSApp(); @@ -267,7 +288,7 @@ impl Window { } pub fn is_closed(&self) -> bool { - self.state.is_closed.load(Relaxed) + self.is_closed.get() } pub fn set_title(&self, title: &str) { @@ -319,7 +340,27 @@ impl Window { NSDefaultRunLoopMode, true); if event == nil { break; } - NSApp().sendEvent_(event); + { + // Create a temporary structure with state that delegates called internally + // by sendEvent can read and modify. When that returns, update window state. + // This allows the synchronous resize loop to continue issuing callbacks + // to the user application, by passing handler through to the delegate state. + let mut ds = DelegateState { + is_closed: self.is_closed.get(), + context: self.context, + view: self.view, + window: self, + handler: self.resize, + }; + object_setInstanceVariable(self.delegate, + DELEGATE_STATE_IVAR.as_ptr() as *const i8, + &mut ds as *mut DelegateState as *mut libc::c_void); + NSApp().sendEvent_(event); + object_setInstanceVariable(self.delegate, + DELEGATE_STATE_IVAR.as_ptr() as *const i8, + ptr::null_mut()); + self.is_closed.set(ds.is_closed); +} match event.get_type() { NSLeftMouseDown => { events.push(MouseInput(Pressed, LeftMouseButton)); }, @@ -434,4 +475,8 @@ impl Window { pub fn get_api(&self) -> ::Api { ::Api::OpenGl } + + pub fn set_window_resize_callback(&mut self, callback: fn(uint, uint)) { + self.resize = callback; + } } diff --git a/src/win32/mod.rs b/src/win32/mod.rs index 978e3b5d..2e0dcae0 100644 --- a/src/win32/mod.rs +++ b/src/win32/mod.rs @@ -46,6 +46,9 @@ impl HeadlessContext { pub fn get_api(&self) -> ::Api { ::Api::OpenGl } + + pub fn set_window_resize_callback(&mut self, _: fn(uint, uint)) { + } } /// The Win32 implementation of the main `Window` object. @@ -276,6 +279,9 @@ impl Window { pub fn get_api(&self) -> ::Api { ::Api::OpenGl } + + pub fn set_window_resize_callback(&mut self, _: fn(uint, uint)) { + } } #[unsafe_destructor] diff --git a/src/x11/headless.rs b/src/x11/headless.rs index 78fb985c..3bcea29f 100644 --- a/src/x11/headless.rs +++ b/src/x11/headless.rs @@ -52,6 +52,9 @@ impl HeadlessContext { pub fn get_api(&self) -> ::Api { ::Api::OpenGl } + + pub fn set_window_resize_callback(&mut self, _: fn(uint, uint)) { + } } impl Drop for HeadlessContext { diff --git a/src/x11/window/mod.rs b/src/x11/window/mod.rs index a0e70784..0a3ba010 100644 --- a/src/x11/window/mod.rs +++ b/src/x11/window/mod.rs @@ -598,4 +598,7 @@ impl Window { pub fn get_api(&self) -> ::Api { ::Api::OpenGl } + + pub fn set_window_resize_callback(&mut self, _: fn(uint, uint)) { + } }