Skip to content

Commit

Permalink
Very rough usage of CFRunLoop
Browse files Browse the repository at this point in the history
  • Loading branch information
francesca64 committed Dec 12, 2018
1 parent 3c7a52f commit 7c7fcc9
Show file tree
Hide file tree
Showing 5 changed files with 522 additions and 48 deletions.
101 changes: 101 additions & 0 deletions src/platform_impl/macos/app_delegate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use cocoa::base::id;
use objc::{runtime::{Class, Object, Sel, BOOL, YES}, declare::ClassDecl};

use platform_impl::platform::event_loop::HANDLER;

pub struct AppDelegateClass(pub *const Class);
unsafe impl Send for AppDelegateClass {}
unsafe impl Sync for AppDelegateClass {}

lazy_static! {
pub static ref APP_DELEGATE_CLASS: AppDelegateClass = unsafe {
let superclass = class!(NSResponder);
let mut decl = ClassDecl::new("WinitAppDelegate", superclass).unwrap();

decl.add_method(
sel!(applicationDidFinishLaunching:),
did_finish_launching as extern fn(&Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(applicationDidBecomeActive:),
did_become_active as extern fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillResignActive:),
will_resign_active as extern fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillEnterForeground:),
will_enter_foreground as extern fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationDidEnterBackground:),
did_enter_background as extern fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillTerminate:),
will_terminate as extern fn(&Object, Sel, id),
);

AppDelegateClass(decl.register())
};
}

extern fn did_finish_launching(_: &Object, _: Sel, _: id) -> BOOL {
trace!("Triggered `didFinishLaunching`");
HANDLER.lock().unwrap().launched();
trace!("Completed `didFinishLaunching`");
YES
}

extern fn did_become_active(_: &Object, _: Sel, _: id) {
trace!("Triggered `didBecomeActive`");
/*unsafe {
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended(false))
}*/
trace!("Completed `didBecomeActive`");
}

extern fn will_resign_active(_: &Object, _: Sel, _: id) {
trace!("Triggered `willResignActive`");
/*unsafe {
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended(true))
}*/
trace!("Completed `willResignActive`");
}

extern fn will_enter_foreground(_: &Object, _: Sel, _: id) {
trace!("Triggered `willEnterForeground`");
trace!("Completed `willEnterForeground`");
}

extern fn did_enter_background(_: &Object, _: Sel, _: id) {
trace!("Triggered `didEnterBackground`");
trace!("Completed `didEnterBackground`");
}

extern fn will_terminate(_: &Object, _: Sel, _: id) {
trace!("Triggered `willTerminate`");
/*unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication];
let windows: id = msg_send![app, windows];
let windows_enum: id = msg_send![windows, objectEnumerator];
let mut events = Vec::new();
loop {
let window: id = msg_send![windows_enum, nextObject];
if window == nil {
break
}
let is_winit_window: BOOL = msg_send![window, isKindOfClass:class!(WinitUIWindow)];
if is_winit_window == YES {
events.push(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Destroyed,
});
}
}
HANDLER.lock().unwrap().handle_nonuser_events(events);
HANDLER.lock().unwrap().terminated();
}*/
trace!("Completed `willTerminate`");
}
201 changes: 154 additions & 47 deletions src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{
collections::VecDeque, hint::unreachable_unchecked, marker::PhantomData,
mem, os::raw::*, process::exit, sync::{Arc, Mutex, Weak},
collections::VecDeque, fmt::{self, Debug, Formatter},
hint::unreachable_unchecked, marker::PhantomData, mem, os::raw::*,
process::exit, sync::{Arc, Mutex, Weak},
};

use cocoa::{
Expand All @@ -21,7 +22,9 @@ use {
window,
};
use platform_impl::platform::{
DEVICE_ID, monitor::{self, MonitorHandle}, window::UnownedWindow,
app_delegate::APP_DELEGATE_CLASS, DEVICE_ID, monitor::{self, MonitorHandle},
observer::{EventLoopWaker, setup_control_flow_observers},
util::IdRef, window::UnownedWindow,
};

#[derive(Default)]
Expand Down Expand Up @@ -87,6 +90,118 @@ impl WindowList {
}
}

lazy_static! {
pub static ref HANDLER: Mutex<Handler> = Default::default();
}

#[derive(Default)]
pub struct Handler {
control_flow: ControlFlow,
control_flow_prev: ControlFlow,
callback: Option<Box<dyn EventHandler>>,
waker: EventLoopWaker,
}

unsafe impl Send for Handler {}
unsafe impl Sync for Handler {}

impl Handler {
pub fn launched(&mut self) {
self.waker.start();
if let Some(ref mut callback) = self.callback {
callback.handle_nonuser_event(Event::NewEvents(StartCause::Init), &mut self.control_flow);
}
}

pub fn wakeup(&mut self) {
self.control_flow_prev = self.control_flow;
let cause = match self.control_flow {
ControlFlow::Poll => StartCause::Poll,
/*ControlFlow::Wait => StartCause::WaitCancelled {
start,
requested_resume: None,
},
ControlFlow::WaitUntil(requested_resume) => {
if Instant::now() >= requested_resume {
StartCause::ResumeTimeReached {
start,
requested_resume,
}
} else {
StartCause::WaitCancelled {
start,
requested_resume: Some(requested_resume),
}
}
},*/
ControlFlow::Exit => panic!("unexpected `ControlFlow::Exit`"),
_ => unimplemented!(),
};
if let Some(ref mut callback) = self.callback {
callback.handle_nonuser_event(Event::NewEvents(cause), &mut self.control_flow);
}
}

pub fn cleared(&mut self) {
if let Some(ref mut callback) = self.callback {
callback.handle_nonuser_event(Event::EventsCleared, &mut self.control_flow);
}
let old = self.control_flow_prev;
let new = self.control_flow;
match (old, new) {
(ControlFlow::Poll, ControlFlow::Poll) => (),
(ControlFlow::Wait, ControlFlow::Wait) => (),
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant)) if old_instant == new_instant => (),
(_, ControlFlow::Wait) => self.waker.stop(),
(_, ControlFlow::WaitUntil(new_instant)) => self.waker.start_at(new_instant),
(_, ControlFlow::Poll) => self.waker.start(),
(_, ControlFlow::Exit) => (),
}
}
}

pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow);
//fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
}

struct EventLoopHandler<F, T: 'static> {
callback: F,
event_loop: RootELW<T>,
}

impl<F, T: 'static> Debug for EventLoopHandler<F, T> {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.debug_struct("EventLoopHandler")
.field("event_loop", &self.event_loop)
.finish()
}
}

impl<F, T> EventHandler for EventLoopHandler<F, T>
where
F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
T: 'static,
{
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
(self.callback)(
event.userify(),
&self.event_loop,
control_flow,
);
}

/*fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
for event in self.event_loop.inner.receiver.try_iter() {
(self.callback)(
Event::UserEvent(event),
&self.event_loop,
control_flow,
);
}
}*/
}

pub struct EventLoopWindowTarget<T: 'static> {
pub pending_events: Arc<Mutex<PendingEvents>>,
pub window_list: Arc<Mutex<WindowList>>,
Expand All @@ -105,19 +220,35 @@ impl<T> Default for EventLoopWindowTarget<T> {

pub struct EventLoop<T: 'static> {
elw_target: RootELW<T>,
delegate: IdRef,
modifiers: Modifiers,
}

impl<T> EventLoop<T> {
pub fn new() -> Self {
// Mark this thread as the main thread of the Cocoa event system.
//
// This must be done before any worker threads get a chance to call it
// (e.g., via `EventLoopProxy::wakeup()`), causing a wrong thread to be
// marked as the main thread.
unsafe { NSApp() };
let delegate = unsafe {
if !msg_send![class!(NSThread), isMainThread] {
// This check should be in `new` instead
panic!("Events can only be polled from the main thread on macOS");
}

// Mark this thread as the main thread of the Cocoa event system.
//
// This must be done before any worker threads get a chance to call it
// (e.g., via `EventLoopProxy::wakeup()`), causing a wrong thread to be
// marked as the main thread.
let app = NSApp();

let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
let pool = NSAutoreleasePool::new(nil);
let _: () = msg_send![app, setDelegate:*delegate];
let _: () = msg_send![pool, drain];
delegate
};
setup_control_flow_observers();
EventLoop {
elw_target: RootELW::new(Default::default()),
delegate, // is this necessary?
modifiers: Default::default(),
}
}
Expand All @@ -139,18 +270,8 @@ impl<T> EventLoop<T> {
pub fn run<F>(mut self, mut callback: F) -> !
where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
{
unsafe {
if !msg_send![class!(NSThread), isMainThread] {
panic!("Events can only be polled from the main thread on macOS");
}
}

let mut control_flow = Default::default();
let mut cause = StartCause::Init;

/*
loop {
callback(Event::NewEvents(cause), self.window_target(), &mut control_flow);

{
trace!("Locked pending events in `run`");
let mut pending = self.elw_target
Expand All @@ -169,38 +290,24 @@ impl<T> EventLoop<T> {
}
}
let maybe_event = unsafe {
let pool = NSAutoreleasePool::new(nil);

// Wait for the next event. Note that this function blocks during resize.
let ns_event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
NSDate::distantFuture(nil),
NSDefaultRunLoopMode,
YES,
);

let maybe_event = self.translate_event(ns_event);

// Release the pool before calling the top callback in case the user calls either
// `run_forever` or `poll_events` within the callback.
let _: () = msg_send![pool, release];

maybe_event
};

if let Some(event) = maybe_event {
callback(event.userify(), self.window_target(), &mut control_flow);
}

callback(Event::EventsCleared, self.window_target(), &mut control_flow);

if let ControlFlow::Exit = control_flow {
callback(Event::LoopDestroyed, self.window_target(), &mut control_flow);
exit(0);
}
}
*/

cause = StartCause::Poll;
unsafe {
let _pool = NSAutoreleasePool::new(nil);
let app = NSApp();
assert!(!app.is_null());
HANDLER.lock().unwrap().callback = Some(Box::new(EventLoopHandler {
callback,
event_loop: self.elw_target,
}));
let _: () = msg_send![app, run];
// This is probably wrong
unreachable_unchecked()
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#![cfg(target_os = "macos")]

mod app_delegate;
mod event_loop;
mod ffi;
mod monitor;
mod observer;
mod util;
mod view;
mod window;
Expand Down
Loading

0 comments on commit 7c7fcc9

Please sign in to comment.