From 2a4b381384c6a81893affe9993f7a578df0ed499 Mon Sep 17 00:00:00 2001
From: tomaka <pierre.krieger1708@gmail.com>
Date: Mon, 4 Sep 2017 12:41:10 +0200
Subject: [PATCH] Update iOS for EventsLoop design (#275)

* Inline gen_api_transition! in the ios module

* Event loop proxy work

* Monitors

* Use freestanding functions

* Finish conversion
---
 src/platform/ios/mod.rs | 515 +++++++++++++++++++++-------------------
 1 file changed, 269 insertions(+), 246 deletions(-)

diff --git a/src/platform/ios/mod.rs b/src/platform/ios/mod.rs
index 8155e527..13e9ff19 100644
--- a/src/platform/ios/mod.rs
+++ b/src/platform/ios/mod.rs
@@ -71,7 +71,9 @@ use objc::runtime::{Class, Object, Sel, BOOL, YES };
 use objc::declare::{ ClassDecl };
 
 use { CreationError, CursorState, MouseCursor, WindowAttributes, FullScreenState };
-use WindowEvent as Event;
+use WindowId as RootEventId;
+use WindowEvent;
+use Event;
 use events::{ Touch, TouchPhase };
 
 mod ffi;
@@ -96,24 +98,16 @@ static mut jmpbuf: [c_int;27] = [0;27];
 #[derive(Clone)]
 pub struct MonitorId;
 
-pub struct Window {
+pub struct Window2 {
     delegate_state: *mut DelegateState
 }
 
 #[derive(Clone)]
 pub struct WindowProxy;
 
-pub struct PollEventsIterator<'a> {
-    window: &'a Window,
-}
-
-pub struct WaitEventsIterator<'a> {
-    window: &'a Window,
-}
-
 #[derive(Debug)]
 struct DelegateState {
-    events_queue: VecDeque<Event>,
+    events_queue: VecDeque<WindowEvent>,
     window: id,
     controller: id,
     size: (u32,u32),
@@ -134,18 +128,6 @@ impl DelegateState {
     }
 }
 
-#[inline]
-pub fn get_available_monitors() -> VecDeque<MonitorId> {
-    let mut rb = VecDeque::new();
-    rb.push_back(MonitorId);
-    rb
-}
-
-#[inline]
-pub fn get_primary_monitor() -> MonitorId {
-    MonitorId
-}
-
 impl MonitorId {
     #[inline]
     pub fn get_name(&self) -> Option<String> {
@@ -158,15 +140,14 @@ impl MonitorId {
     }
 }
 
-gen_api_transition!();
+pub struct EventsLoop {
+    delegate_state: *mut DelegateState
+}
 
-#[derive(Clone, Default)]
-pub struct PlatformSpecificWindowBuilderAttributes;
+pub struct EventsLoopProxy;
 
-impl Window {
-
-    pub fn new(_: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
-    {
+impl EventsLoop {
+    pub fn new() -> EventsLoop {
         unsafe {
             if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
                 let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication];
@@ -174,188 +155,118 @@ impl Window {
                 let state: *mut c_void = *(&*delegate).get_ivar("glutinState");
                 let state = state as *mut DelegateState;
 
-                let window = Window {
+                let events_loop = EventsLoop {
                     delegate_state: state
                 };
 
-                return Ok(window)
+                return events_loop;
             }
         }
 
-        Window::create_delegate_class();
-        Window::create_view_class();
-        Window::start_app();
+        create_delegate_class();
+        create_view_class();
+        start_app();
 
-        Err(CreationError::OsError(format!("Couldn't create UIApplication")))
-    }
-
-    fn create_delegate_class() {
-        extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
-            unsafe {
-                let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
-                let bounds: CGRect = msg_send![main_screen, bounds];
-                let scale: CGFloat = msg_send![main_screen, nativeScale];
-
-                let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
-                let window: id = msg_send![window, initWithFrame:bounds.clone()];
-
-                let size = (bounds.size.width as u32, bounds.size.height as u32);
-
-                let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
-                let view_controller: id = msg_send![view_controller, init];
-
-                let _: () = msg_send![window, setRootViewController:view_controller];
-                let _: () = msg_send![window, makeKeyAndVisible];
-
-                let state = Box::new(DelegateState::new(window, view_controller, size, scale as f32));
-                let state_ptr: *mut DelegateState = mem::transmute(state);
-                this.set_ivar("glutinState", state_ptr as *mut c_void);
-
-
-                let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
-            }
-            YES
-        }
-
-        extern fn post_launch(_: &Object, _: Sel, _: id) {
-            unsafe { longjmp(mem::transmute(&mut jmpbuf),1); }
-        }
-
-        extern fn did_become_active(this: &Object, _: Sel, _: id) {
-            unsafe {
-                let state: *mut c_void = *this.get_ivar("glutinState");
-                let state = &mut *(state as *mut DelegateState);
-                state.events_queue.push_back(Event::Focused(true));
-            }
-        }
-
-        extern fn will_resign_active(this: &Object, _: Sel, _: id) {
-            unsafe {
-                let state: *mut c_void = *this.get_ivar("glutinState");
-                let state = &mut *(state as *mut DelegateState);
-                state.events_queue.push_back(Event::Focused(false));
-            }
-        }
-
-        extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
-            unsafe {
-                let state: *mut c_void = *this.get_ivar("glutinState");
-                let state = &mut *(state as *mut DelegateState);
-                state.events_queue.push_back(Event::Suspended(false));
-            }
-        }
-
-        extern fn did_enter_background(this: &Object, _: Sel, _: id) {
-            unsafe {
-                let state: *mut c_void = *this.get_ivar("glutinState");
-                let state = &mut *(state as *mut DelegateState);
-                state.events_queue.push_back(Event::Suspended(true));
-            }
-        }
-
-        extern fn will_terminate(this: &Object, _: Sel, _: id) {
-            unsafe {
-                let state: *mut c_void = *this.get_ivar("glutinState");
-                let state = &mut *(state as *mut DelegateState);
-                // push event to the front to garantee that we'll process it
-                // immidiatly after jump
-                state.events_queue.push_front(Event::Closed);
-                longjmp(mem::transmute(&mut jmpbuf),1);
-            }
-        }
-
-        extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
-            unsafe {
-                let state: *mut c_void = *this.get_ivar("glutinState");
-                let state = &mut *(state as *mut DelegateState);
-
-                let touches_enum: id = msg_send![touches, objectEnumerator];
-
-                loop {
-                    let touch: id = msg_send![touches_enum, nextObject];
-                    if touch == nil {
-                        break
-                    }
-                    let location: CGPoint = msg_send![touch, locationInView:nil];
-                    let touch_id = touch as u64;
-                    let phase: i32 = msg_send![touch, phase];
-
-                    state.events_queue.push_back(Event::Touch(Touch {
-                        device_id: DEVICE_ID,
-                        id: touch_id,
-                        location: (location.x as f64, location.y as f64),
-                        phase: match phase {
-                            0 => TouchPhase::Started,
-                            1 => TouchPhase::Moved,
-                            // 2 is UITouchPhaseStationary and is not expected here
-                            3 => TouchPhase::Ended,
-                            4 => TouchPhase::Cancelled,
-                            _ => panic!("unexpected touch phase: {:?}", phase)
-                        }
-                    }));
-                }
-            }
-        }
-
-        let ui_responder = Class::get("UIResponder").unwrap();
-        let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
-
-        unsafe {
-            decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
-                            did_finish_launching as extern fn(&mut Object, Sel, id, 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));
-
-
-            decl.add_method(sel!(touchesBegan:withEvent:),
-                            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
-
-            decl.add_method(sel!(touchesMoved:withEvent:),
-                            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
-
-            decl.add_method(sel!(touchesEnded:withEvent:),
-                            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
-
-            decl.add_method(sel!(touchesCancelled:withEvent:),
-                            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
-
-
-            decl.add_method(sel!(postLaunch:),
-                            post_launch as extern fn(&Object, Sel, id));
-
-            decl.add_ivar::<*mut c_void>("glutinState");
-
-            decl.register();
-        }
-    }
-
-    fn create_view_class() {
-        let ui_view_controller = Class::get("UIViewController").unwrap();
-        let decl = ClassDecl::new("MainViewController", ui_view_controller).unwrap();
-
-        decl.register();
+        panic!("Couldn't create UIApplication")
     }
 
     #[inline]
-    fn start_app() {
+    pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
+        let mut rb = VecDeque::new();
+        rb.push_back(MonitorId);
+        rb
+    }
+
+    #[inline]
+    pub fn get_primary_monitor(&self) -> MonitorId {
+        MonitorId
+    }
+
+    pub fn poll_events<F>(&mut self, mut callback: F)
+        where F: FnMut(::Event)
+    {
         unsafe {
-            UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate"));
+            let state = &mut *self.delegate_state;
+
+            if let Some(event) = state.events_queue.pop_front() {
+                callback(Event::WindowEvent {
+                    window_id: RootEventId(WindowId),
+                    event: event,
+                });
+                return;
+            }
+
+            // jump hack, so we won't quit on willTerminate event before processing it
+            if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
+                if let Some(event) = state.events_queue.pop_front() {
+                    callback(Event::WindowEvent {
+                        window_id: RootEventId(WindowId),
+                        event: event,
+                    });
+                    return;
+                }
+            }
+
+            // run runloop
+            let seconds: CFTimeInterval = 0.000002;
+            while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
+
+            if let Some(event) = state.events_queue.pop_front() {
+                callback(Event::WindowEvent {
+                    window_id: RootEventId(WindowId),
+                    event: event,
+                })
+            }
         }
     }
 
+    pub fn run_forever<F>(&mut self, mut callback: F)
+        where F: FnMut(::Event) -> ::ControlFlow,
+    {
+        // Yeah that's a very bad implementation.
+        loop {
+            let mut control_flow = ::ControlFlow::Continue;
+            self.poll_events(|e| {
+                if let ::ControlFlow::Break = callback(e) {
+                    control_flow = ::ControlFlow::Break;
+                }
+            });
+            if let ::ControlFlow::Break = control_flow {
+                break;
+            }
+            ::std::thread::sleep(::std::time::Duration::from_millis(5));
+        }
+    }
+
+    pub fn create_proxy(&self) -> EventsLoopProxy {
+        EventsLoopProxy
+    }
+}
+
+impl EventsLoopProxy {
+    pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> {
+        unimplemented!()
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct WindowId;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct DeviceId;
+
+#[derive(Clone, Default)]
+pub struct PlatformSpecificWindowBuilderAttributes;
+
+impl Window2 {
+    pub fn new(ev: &EventsLoop, _: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
+               -> Result<Window2, CreationError>
+    {
+        Ok(Window2 {
+            delegate_state: ev.delegate_state,
+        })
+    }
+
     #[inline]
     pub fn set_title(&self, _: &str) {
     }
@@ -391,20 +302,6 @@ impl Window {
     pub fn set_inner_size(&self, _x: u32, _y: u32) {
     }
 
-    #[inline]
-    pub fn poll_events(&self) -> PollEventsIterator {
-        PollEventsIterator {
-            window: self
-        }
-    }
-
-    #[inline]
-    pub fn wait_events(&self) -> WaitEventsIterator {
-        WaitEventsIterator {
-            window: self
-        }
-    }
-
     #[inline]
     pub fn platform_display(&self) -> *mut libc::c_void {
         unimplemented!();
@@ -450,51 +347,177 @@ impl Window {
     #[inline]
     pub fn set_fullscreen(&self, state: FullScreenState) {
     }
-}
 
-impl WindowProxy {
     #[inline]
-    pub fn wakeup_event_loop(&self) {
-        unimplemented!()
+    pub fn id(&self) -> WindowId {
+        WindowId
     }
 }
 
-
-impl<'a> Iterator for WaitEventsIterator<'a> {
-    type Item = Event;
-
-    #[inline]
-    fn next(&mut self) -> Option<Event> {
-        loop {
-            if let Some(ev) = self.window.poll_events().next() {
-                return Some(ev);
-            }
-        }
-    }
-}
-
-impl<'a> Iterator for PollEventsIterator<'a> {
-    type Item = Event;
-
-    fn next(&mut self) -> Option<Event> {
+fn create_delegate_class() {
+    extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
         unsafe {
-            let state = &mut *self.window.delegate_state;
+            let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
+            let bounds: CGRect = msg_send![main_screen, bounds];
+            let scale: CGFloat = msg_send![main_screen, nativeScale];
 
-            if let Some(event) = state.events_queue.pop_front() {
-                return Some(event)
-            }
+            let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
+            let window: id = msg_send![window, initWithFrame:bounds.clone()];
 
-            // jump hack, so we won't quit on willTerminate event before processing it
-            if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
-                return state.events_queue.pop_front()
-            }
+            let size = (bounds.size.width as u32, bounds.size.height as u32);
 
-            // run runloop
-            let seconds: CFTimeInterval = 0.000002;
-            while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
+            let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
+            let view_controller: id = msg_send![view_controller, init];
 
-            state.events_queue.pop_front()
+            let _: () = msg_send![window, setRootViewController:view_controller];
+            let _: () = msg_send![window, makeKeyAndVisible];
+
+            let state = Box::new(DelegateState::new(window, view_controller, size, scale as f32));
+            let state_ptr: *mut DelegateState = mem::transmute(state);
+            this.set_ivar("glutinState", state_ptr as *mut c_void);
+
+
+            let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
         }
+        YES
+    }
+
+    extern fn post_launch(_: &Object, _: Sel, _: id) {
+        unsafe { longjmp(mem::transmute(&mut jmpbuf),1); }
+    }
+
+    extern fn did_become_active(this: &Object, _: Sel, _: id) {
+        unsafe {
+            let state: *mut c_void = *this.get_ivar("glutinState");
+            let state = &mut *(state as *mut DelegateState);
+            state.events_queue.push_back(WindowEvent::Focused(true));
+        }
+    }
+
+    extern fn will_resign_active(this: &Object, _: Sel, _: id) {
+        unsafe {
+            let state: *mut c_void = *this.get_ivar("glutinState");
+            let state = &mut *(state as *mut DelegateState);
+            state.events_queue.push_back(WindowEvent::Focused(false));
+        }
+    }
+
+    extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
+        unsafe {
+            let state: *mut c_void = *this.get_ivar("glutinState");
+            let state = &mut *(state as *mut DelegateState);
+            state.events_queue.push_back(WindowEvent::Suspended(false));
+        }
+    }
+
+    extern fn did_enter_background(this: &Object, _: Sel, _: id) {
+        unsafe {
+            let state: *mut c_void = *this.get_ivar("glutinState");
+            let state = &mut *(state as *mut DelegateState);
+            state.events_queue.push_back(WindowEvent::Suspended(true));
+        }
+    }
+
+    extern fn will_terminate(this: &Object, _: Sel, _: id) {
+        unsafe {
+            let state: *mut c_void = *this.get_ivar("glutinState");
+            let state = &mut *(state as *mut DelegateState);
+            // push event to the front to garantee that we'll process it
+            // immidiatly after jump
+            state.events_queue.push_front(WindowEvent::Closed);
+            longjmp(mem::transmute(&mut jmpbuf),1);
+        }
+    }
+
+    extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
+        unsafe {
+            let state: *mut c_void = *this.get_ivar("glutinState");
+            let state = &mut *(state as *mut DelegateState);
+
+            let touches_enum: id = msg_send![touches, objectEnumerator];
+
+            loop {
+                let touch: id = msg_send![touches_enum, nextObject];
+                if touch == nil {
+                    break
+                }
+                let location: CGPoint = msg_send![touch, locationInView:nil];
+                let touch_id = touch as u64;
+                let phase: i32 = msg_send![touch, phase];
+
+                state.events_queue.push_back(WindowEvent::Touch(Touch {
+                    device_id: DEVICE_ID,
+                    id: touch_id,
+                    location: (location.x as f64, location.y as f64),
+                    phase: match phase {
+                        0 => TouchPhase::Started,
+                        1 => TouchPhase::Moved,
+                        // 2 is UITouchPhaseStationary and is not expected here
+                        3 => TouchPhase::Ended,
+                        4 => TouchPhase::Cancelled,
+                        _ => panic!("unexpected touch phase: {:?}", phase)
+                    }
+                }));
+            }
+        }
+    }
+
+    let ui_responder = Class::get("UIResponder").unwrap();
+    let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
+
+    unsafe {
+        decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
+                        did_finish_launching as extern fn(&mut Object, Sel, id, 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));
+
+
+        decl.add_method(sel!(touchesBegan:withEvent:),
+                        handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
+
+        decl.add_method(sel!(touchesMoved:withEvent:),
+                        handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
+
+        decl.add_method(sel!(touchesEnded:withEvent:),
+                        handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
+
+        decl.add_method(sel!(touchesCancelled:withEvent:),
+                        handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
+
+
+        decl.add_method(sel!(postLaunch:),
+                        post_launch as extern fn(&Object, Sel, id));
+
+        decl.add_ivar::<*mut c_void>("glutinState");
+
+        decl.register();
+    }
+}
+
+fn create_view_class() {
+    let ui_view_controller = Class::get("UIViewController").unwrap();
+    let decl = ClassDecl::new("MainViewController", ui_view_controller).unwrap();
+
+    decl.register();
+}
+
+#[inline]
+fn start_app() {
+    unsafe {
+        UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate"));
     }
 }