Complete macos backend update to addition of ControlFlow

This commit is contained in:
mitchmindtree 2017-06-09 22:13:30 +10:00
parent c5b9bd3612
commit 0237526999
3 changed files with 121 additions and 97 deletions

View file

@ -2,16 +2,22 @@ use {ControlFlow, EventsLoopClosed};
use cocoa::{self, appkit, foundation}; use cocoa::{self, appkit, foundation};
use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow}; use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow};
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState, KeyboardInput}; use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState, KeyboardInput};
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, Weak};
use super::window::Window; use super::window::Window;
use std; use std;
use super::DeviceId; use super::DeviceId;
pub struct EventsLoop { pub struct EventsLoop {
pub windows: std::sync::Mutex<Vec<std::sync::Weak<Window>>>, modifiers: Modifiers,
pub pending_events: std::sync::Mutex<std::collections::VecDeque<Event>>, pub shared: Arc<Shared>,
modifiers: std::sync::Mutex<Modifiers>, }
// State shared between the `EventsLoop` and its registered windows.
pub struct Shared {
pub windows: Mutex<Vec<Weak<Window>>>,
pub pending_events: Mutex<VecDeque<Event>>,
// The user event callback given via either of the `poll_events` or `run_forever` methods. // The user event callback given via either of the `poll_events` or `run_forever` methods.
// //
// We store the user's callback here so that it may be accessed by each of the window delegate // We store the user's callback here so that it may be accessed by each of the window delegate
@ -36,9 +42,74 @@ struct Modifiers {
// //
// - ensure the callback pointer is never accidentally cloned // - ensure the callback pointer is never accidentally cloned
// - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer // - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer
// - `unsafe impl Send` and `Sync` so that `Send` and `Sync` can be implemented for `EventsLoop`. // - Share access to the user callback with the NSWindow callbacks.
pub struct UserCallback { pub struct UserCallback {
mutex: std::sync::Mutex<Option<*mut FnMut(Event)>>, mutex: Mutex<Option<*mut FnMut(Event)>>,
}
impl Shared {
pub fn new() -> Self {
Shared {
windows: Mutex::new(Vec::new()),
pending_events: Mutex::new(VecDeque::new()),
user_callback: UserCallback { mutex: Mutex::new(None) },
}
}
fn call_user_callback_with_pending_events(&self) {
loop {
let event = match self.pending_events.lock().unwrap().pop_front() {
Some(event) => event,
None => return,
};
unsafe {
self.user_callback.call_with_event(event);
}
}
}
// Calls the user callback if one exists.
//
// Otherwise, stores the event in the `pending_events` queue.
//
// This is necessary for the case when `WindowDelegate` callbacks are triggered during a call
// to the user's callback.
pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) {
if self.user_callback.mutex.lock().unwrap().is_some() {
unsafe {
self.user_callback.call_with_event(event);
}
} else {
self.pending_events.lock().unwrap().push_back(event);
}
}
// Removes the window with the given `Id` from the `windows` list.
//
// This is called when a window is either `Closed` or `Drop`ped.
pub fn find_and_remove_window(&self, id: super::window::Id) {
if let Ok(mut windows) = self.windows.lock() {
windows.retain(|w| match w.upgrade() {
Some(w) => w.id() != id,
None => true,
});
}
}
}
impl Modifiers {
pub fn new() -> Self {
Modifiers {
shift_pressed: false,
ctrl_pressed: false,
win_pressed: false,
alt_pressed: false,
}
}
} }
@ -91,17 +162,9 @@ impl UserCallback {
impl EventsLoop { impl EventsLoop {
pub fn new() -> Self { pub fn new() -> Self {
let modifiers = Modifiers {
shift_pressed: false,
ctrl_pressed: false,
win_pressed: false,
alt_pressed: false,
};
EventsLoop { EventsLoop {
windows: std::sync::Mutex::new(Vec::new()), shared: Arc::new(Shared::new()),
pending_events: std::sync::Mutex::new(std::collections::VecDeque::new()), modifiers: Modifiers::new(),
modifiers: std::sync::Mutex::new(modifiers),
user_callback: UserCallback { mutex: std::sync::Mutex::new(None) },
} }
} }
@ -114,13 +177,13 @@ impl EventsLoop {
} }
} }
self.user_callback.store(&mut callback); self.shared.user_callback.store(&mut callback);
// Loop as long as we have pending events to return. // Loop as long as we have pending events to return.
loop { loop {
unsafe { unsafe {
// First, yield all pending events. // First, yield all pending events.
self.call_user_callback_with_pending_events(); self.shared.call_user_callback_with_pending_events();
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil); let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
@ -137,13 +200,13 @@ impl EventsLoop {
match event { match event {
// Call the user's callback. // Call the user's callback.
Some(event) => self.user_callback.call_with_event(event), Some(event) => self.shared.user_callback.call_with_event(event),
None => break, None => break,
} }
} }
} }
self.user_callback.drop(); self.shared.user_callback.drop();
} }
pub fn run_forever<F>(&mut self, mut callback: F) pub fn run_forever<F>(&mut self, mut callback: F)
@ -156,7 +219,7 @@ impl EventsLoop {
} }
// Track whether or not control flow has changed. // Track whether or not control flow has changed.
let mut control_flow = std::cell::Cell::new(ControlFlow::Continue); let control_flow = std::cell::Cell::new(ControlFlow::Continue);
let mut callback = |event| { let mut callback = |event| {
if let ControlFlow::Complete = callback(event) { if let ControlFlow::Complete = callback(event) {
@ -164,12 +227,12 @@ impl EventsLoop {
} }
}; };
self.user_callback.store(&mut callback); self.shared.user_callback.store(&mut callback);
loop { loop {
unsafe { unsafe {
// First, yield all pending events. // First, yield all pending events.
self.call_user_callback_with_pending_events(); self.shared.call_user_callback_with_pending_events();
if let ControlFlow::Complete = control_flow.get() { if let ControlFlow::Complete = control_flow.get() {
break; break;
} }
@ -190,7 +253,7 @@ impl EventsLoop {
let _: () = msg_send![pool, release]; let _: () = msg_send![pool, release];
if let Some(event) = maybe_event { if let Some(event) = maybe_event {
self.user_callback.call_with_event(event); self.shared.user_callback.call_with_event(event);
if let ControlFlow::Complete = control_flow.get() { if let ControlFlow::Complete = control_flow.get() {
break; break;
} }
@ -198,51 +261,11 @@ impl EventsLoop {
} }
} }
self.user_callback.drop(); self.shared.user_callback.drop();
}
// Removes the window with the given `Id` from the `windows` list.
//
// This is called when a window is either `Closed` or `Drop`ped.
pub fn find_and_remove_window(&self, id: super::window::Id) {
if let Ok(mut windows) = self.windows.lock() {
windows.retain(|w| match w.upgrade() {
Some(w) => w.id() != id,
None => true,
});
}
}
fn call_user_callback_with_pending_events(&self) {
loop {
let event = match self.pending_events.lock().unwrap().pop_front() {
Some(event) => event,
None => return,
};
unsafe {
self.user_callback.call_with_event(event);
}
}
}
// Calls the user callback if one exists.
//
// Otherwise, stores the event in the `pending_events` queue.
//
// This is necessary for the case when `WindowDelegate` callbacks are triggered during a call
// to the user's callback.
pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) {
if self.user_callback.mutex.lock().unwrap().is_some() {
unsafe {
self.user_callback.call_with_event(event);
}
} else {
self.pending_events.lock().unwrap().push_back(event);
}
} }
// Convert some given `NSEvent` into a winit `Event`. // Convert some given `NSEvent` into a winit `Event`.
unsafe fn ns_event_to_event(&self, ns_event: cocoa::base::id) -> Option<Event> { unsafe fn ns_event_to_event(&mut self, ns_event: cocoa::base::id) -> Option<Event> {
if ns_event == cocoa::base::nil { if ns_event == cocoa::base::nil {
return None; return None;
} }
@ -268,9 +291,9 @@ impl EventsLoop {
_ => appkit::NSApp().sendEvent_(ns_event), _ => appkit::NSApp().sendEvent_(ns_event),
} }
let windows = self.windows.lock().unwrap(); let windows = self.shared.windows.lock().unwrap();
let maybe_window = windows.iter() let maybe_window = windows.iter()
.filter_map(std::sync::Weak::upgrade) .filter_map(Weak::upgrade)
.find(|window| window_id == window.id()); .find(|window| window_id == window.id());
let into_event = |window_event| Event::WindowEvent { let into_event = |window_event| Event::WindowEvent {
@ -280,7 +303,7 @@ impl EventsLoop {
// Returns `Some` window if one of our windows is the key window. // Returns `Some` window if one of our windows is the key window.
let maybe_key_window = || windows.iter() let maybe_key_window = || windows.iter()
.filter_map(std::sync::Weak::upgrade) .filter_map(Weak::upgrade)
.find(|window| { .find(|window| {
let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow]; let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow];
is_key_window == cocoa::base::YES is_key_window == cocoa::base::YES
@ -309,7 +332,7 @@ impl EventsLoop {
let window_event = WindowEvent::ReceivedCharacter(received_char); let window_event = WindowEvent::ReceivedCharacter(received_char);
events.push_back(into_event(window_event)); events.push_back(into_event(window_event));
} }
self.pending_events.lock().unwrap().extend(events.into_iter()); self.shared.pending_events.lock().unwrap().extend(events.into_iter());
Some(into_event(window_event)) Some(into_event(window_event))
}, },
@ -331,8 +354,6 @@ impl EventsLoop {
}, },
appkit::NSFlagsChanged => { appkit::NSFlagsChanged => {
let mut modifiers = self.modifiers.lock().unwrap();
unsafe fn modifier_event(event: cocoa::base::id, unsafe fn modifier_event(event: cocoa::base::id,
keymask: appkit::NSEventModifierFlags, keymask: appkit::NSEventModifierFlags,
key: events::VirtualKeyCode, key: events::VirtualKeyCode,
@ -375,41 +396,41 @@ impl EventsLoop {
if let Some(window_event) = modifier_event(ns_event, if let Some(window_event) = modifier_event(ns_event,
appkit::NSShiftKeyMask, appkit::NSShiftKeyMask,
events::VirtualKeyCode::LShift, events::VirtualKeyCode::LShift,
modifiers.shift_pressed) self.modifiers.shift_pressed)
{ {
modifiers.shift_pressed = !modifiers.shift_pressed; self.modifiers.shift_pressed = !self.modifiers.shift_pressed;
events.push_back(into_event(window_event)); events.push_back(into_event(window_event));
} }
if let Some(window_event) = modifier_event(ns_event, if let Some(window_event) = modifier_event(ns_event,
appkit::NSControlKeyMask, appkit::NSControlKeyMask,
events::VirtualKeyCode::LControl, events::VirtualKeyCode::LControl,
modifiers.ctrl_pressed) self.modifiers.ctrl_pressed)
{ {
modifiers.ctrl_pressed = !modifiers.ctrl_pressed; self.modifiers.ctrl_pressed = !self.modifiers.ctrl_pressed;
events.push_back(into_event(window_event)); events.push_back(into_event(window_event));
} }
if let Some(window_event) = modifier_event(ns_event, if let Some(window_event) = modifier_event(ns_event,
appkit::NSCommandKeyMask, appkit::NSCommandKeyMask,
events::VirtualKeyCode::LWin, events::VirtualKeyCode::LWin,
modifiers.win_pressed) self.modifiers.win_pressed)
{ {
modifiers.win_pressed = !modifiers.win_pressed; self.modifiers.win_pressed = !self.modifiers.win_pressed;
events.push_back(into_event(window_event)); events.push_back(into_event(window_event));
} }
if let Some(window_event) = modifier_event(ns_event, if let Some(window_event) = modifier_event(ns_event,
appkit::NSAlternateKeyMask, appkit::NSAlternateKeyMask,
events::VirtualKeyCode::LAlt, events::VirtualKeyCode::LAlt,
modifiers.alt_pressed) self.modifiers.alt_pressed)
{ {
modifiers.alt_pressed = !modifiers.alt_pressed; self.modifiers.alt_pressed = !self.modifiers.alt_pressed;
events.push_back(into_event(window_event)); events.push_back(into_event(window_event));
} }
let event = events.pop_front(); let event = events.pop_front();
self.pending_events.lock().unwrap().extend(events.into_iter()); self.shared.pending_events.lock().unwrap().extend(events.into_iter());
event event
}, },

View file

@ -3,6 +3,7 @@
pub use self::events_loop::{EventsLoop, Proxy as EventsLoopProxy}; pub use self::events_loop::{EventsLoop, Proxy as EventsLoopProxy};
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor}; pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window}; pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window};
use std::sync::Arc;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId; pub struct DeviceId;
@ -10,7 +11,7 @@ pub struct DeviceId;
use {CreationError}; use {CreationError};
pub struct Window2 { pub struct Window2 {
pub window: ::std::sync::Arc<Window>, pub window: Arc<Window>,
} }
impl ::std::ops::Deref for Window2 { impl ::std::ops::Deref for Window2 {
@ -23,14 +24,14 @@ impl ::std::ops::Deref for Window2 {
impl Window2 { impl Window2 {
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, pub fn new(events_loop: &EventsLoop,
attributes: &::WindowAttributes, attributes: &::WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError> pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
{ {
let weak_events_loop = ::std::sync::Arc::downgrade(&events_loop); let weak_shared = Arc::downgrade(&events_loop.shared);
let window = ::std::sync::Arc::new(try!(Window::new(weak_events_loop, attributes, pl_attribs))); let window = Arc::new(try!(Window::new(weak_shared, attributes, pl_attribs)));
let weak_window = ::std::sync::Arc::downgrade(&window); let weak_window = Arc::downgrade(&window);
events_loop.windows.lock().unwrap().push(weak_window); events_loop.shared.windows.lock().unwrap().push(weak_window);
Ok(Window2 { window: window }) Ok(Window2 { window: window })
} }

View file

@ -5,6 +5,7 @@ use libc;
use WindowAttributes; use WindowAttributes;
use native_monitor::NativeMonitorId; use native_monitor::NativeMonitorId;
use os::macos::ActivationPolicy; use os::macos::ActivationPolicy;
use os::macos::WindowExt;
use objc; use objc;
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
@ -20,8 +21,9 @@ use core_graphics::display::{CGAssociateMouseAndMouseCursorPosition, CGMainDispl
use std; use std;
use std::ops::Deref; use std::ops::Deref;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::sync::Weak;
use os::macos::WindowExt; use super::events_loop::Shared;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -30,7 +32,7 @@ pub struct Id(pub usize);
struct DelegateState { struct DelegateState {
view: IdRef, view: IdRef,
window: IdRef, window: IdRef,
events_loop: std::sync::Weak<super::EventsLoop>, shared: Weak<Shared>,
} }
pub struct WindowDelegate { pub struct WindowDelegate {
@ -51,8 +53,8 @@ impl WindowDelegate {
event: window_event, event: window_event,
}; };
if let Some(events_loop) = state.events_loop.upgrade() { if let Some(shared) = state.shared.upgrade() {
events_loop.call_user_callback_with_event_or_store_in_pending(event); shared.call_user_callback_with_event_or_store_in_pending(event);
} }
} }
@ -71,10 +73,10 @@ impl WindowDelegate {
let state = &mut *(state as *mut DelegateState); let state = &mut *(state as *mut DelegateState);
emit_event(state, WindowEvent::Closed); emit_event(state, WindowEvent::Closed);
// Remove the window from the events_loop. // Remove the window from the shared state.
if let Some(events_loop) = state.events_loop.upgrade() { if let Some(shared) = state.shared.upgrade() {
let window_id = get_window_id(*state.window); let window_id = get_window_id(*state.window);
events_loop.find_and_remove_window(window_id); shared.find_and_remove_window(window_id);
} }
} }
YES YES
@ -188,8 +190,8 @@ impl Drop for Window {
fn drop(&mut self) { fn drop(&mut self) {
// Remove this window from the `EventLoop`s list of windows. // Remove this window from the `EventLoop`s list of windows.
let id = self.id(); let id = self.id();
if let Some(ev) = self.delegate.state.events_loop.upgrade() { if let Some(shared) = self.delegate.state.shared.upgrade() {
ev.find_and_remove_window(id); shared.find_and_remove_window(id);
} }
// Close the window if it has not yet been closed. // Close the window if it has not yet been closed.
@ -215,7 +217,7 @@ impl WindowExt for Window {
} }
impl Window { impl Window {
pub fn new(events_loop: std::sync::Weak<super::EventsLoop>, pub fn new(shared: Weak<Shared>,
win_attribs: &WindowAttributes, win_attribs: &WindowAttributes,
pl_attribs: &PlatformSpecificWindowBuilderAttributes) pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError> -> Result<Window, CreationError>
@ -266,7 +268,7 @@ impl Window {
let ds = DelegateState { let ds = DelegateState {
view: view.clone(), view: view.clone(),
window: window.clone(), window: window.clone(),
events_loop: events_loop, shared: shared,
}; };
let window = Window { let window = Window {