diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/ios/delegate.rs | 226 | ||||
| -rw-r--r-- | src/api/ios/ffi.rs | 130 | ||||
| -rw-r--r-- | src/api/ios/mod.rs | 420 | ||||
| -rw-r--r-- | src/api/mod.rs | 1 | ||||
| -rw-r--r-- | src/api/wgl/mod.rs | 39 | ||||
| -rw-r--r-- | src/api/win32/callback.rs | 22 | ||||
| -rw-r--r-- | src/api/win32/init.rs | 3 | ||||
| -rw-r--r-- | src/events.rs | 55 | ||||
| -rw-r--r-- | src/lib.rs | 7 | ||||
| -rw-r--r-- | src/platform/ios/mod.rs | 47 | ||||
| -rw-r--r-- | src/platform/mod.rs | 5 | ||||
| -rw-r--r-- | src/window.rs | 6 | 
12 files changed, 933 insertions, 28 deletions
diff --git a/src/api/ios/delegate.rs b/src/api/ios/delegate.rs new file mode 100644 index 0000000..62cd89d --- /dev/null +++ b/src/api/ios/delegate.rs @@ -0,0 +1,226 @@ +use libc; +use std::mem; +use super::DelegateState; +use Event; +use events::{ Touch, TouchPhase }; + +use objc::runtime::{ Class, Object, Sel, BOOL, YES }; +use objc::declare::{ ClassDecl }; + +use super::ffi::{ +    longjmp, +    id, +    nil, +    CGRect, +    CGPoint, +    CGFloat, +    UIViewAutoresizingFlexibleWidth, +    UIViewAutoresizingFlexibleHeight + }; + +use super::jmpbuf; + + +pub 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 class = Class::get("MainView").unwrap(); +            let view:id = msg_send![class, alloc]; +            let view:id = msg_send![view, initForGl:&bounds]; + + +            let _: () = msg_send![view_controller, setView:view]; + + +            let _: () = msg_send![window, setRootViewController:view_controller]; + +            let _: () = msg_send![window, addSubview:view]; +            let _: () = msg_send![window, makeKeyAndVisible]; + +            let state = Box::new(DelegateState::new(window, view_controller, view, size, scale as f32)); +            let state_ptr: *mut DelegateState = mem::transmute(state); +            this.set_ivar("glutinState", state_ptr as *mut libc::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 libc::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 libc::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 libc::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 libc::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 libc::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 libc::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 { +                    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 superclass = Class::get("UIResponder").unwrap(); +    let mut decl = ClassDecl::new(superclass, "AppDelegate").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 libc::c_void>("glutinState"); + +        decl.register(); +    } +} + + +pub fn create_view_class() { +    let superclass = Class::get("UIViewController").unwrap(); +    let decl = ClassDecl::new(superclass, "MainViewController").unwrap(); + +    decl.register(); + +    extern fn init_for_gl(this: &Object, _: Sel, frame: *const libc::c_void) -> id { +        unsafe { +            let bounds: *const CGRect = mem::transmute(frame); +            let view: id = msg_send![this, initWithFrame:(*bounds).clone()]; + +            let _: () = msg_send![view, setAutoresizingMask: UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; +            let _: () = msg_send![view, setAutoresizesSubviews:YES]; + +            let layer: id = msg_send![view, layer]; +            let _ : () = msg_send![layer, setOpaque:YES]; + +            view +        } +    } + +    extern fn layer_class(_: &Class, _: Sel) -> *const Class { +        unsafe { mem::transmute(Class::get("CAEAGLLayer").unwrap()) } +    } + + +    let superclass = Class::get("UIView").unwrap(); +    let mut decl = ClassDecl::new(superclass, "MainView").unwrap(); + +    unsafe { +        decl.add_method(sel!(initForGl:), +            init_for_gl as extern fn(&Object, Sel, *const libc::c_void) -> id); + +        decl.add_class_method(sel!(layerClass), +            layer_class as extern fn(&Class, Sel) -> *const Class); +        decl.register(); +    } +}
\ No newline at end of file diff --git a/src/api/ios/ffi.rs b/src/api/ios/ffi.rs new file mode 100644 index 0000000..72f2ff2 --- /dev/null +++ b/src/api/ios/ffi.rs @@ -0,0 +1,130 @@ +use std::ffi::CString; + +use libc; +use objc::runtime::{ Object, Class }; + +#[allow(non_camel_case_types)] +pub type id = *mut Object; + +#[allow(non_camel_case_types)] +#[allow(non_upper_case_globals)] +pub const nil: id = 0 as id; + +pub type CFStringRef = *const libc::c_void; +pub type CFTimeInterval = f64; +pub type Boolean = u32; + +#[allow(non_upper_case_globals)] +pub const kCFRunLoopRunHandledSource: i32 = 4; + +#[cfg(target_pointer_width = "32")] +pub type CGFloat = f32; +#[cfg(target_pointer_width = "64")] +pub type CGFloat = f64; + +#[cfg(target_pointer_width = "32")] +pub type NSUInteger = u32; +#[cfg(target_pointer_width = "64")] +pub type NSUInteger = u64; + +#[allow(non_upper_case_globals)] +pub const UIViewAutoresizingFlexibleWidth: NSUInteger = 1 << 1; +#[allow(non_upper_case_globals)] +pub const UIViewAutoresizingFlexibleHeight: NSUInteger = 1 << 4; + + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct CGPoint { +    pub x: CGFloat, +    pub y: CGFloat, +} + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct CGRect { +    pub origin: CGPoint, +    pub size: CGSize +} + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct CGSize { +    pub width: CGFloat, +    pub height: CGFloat +} + +pub mod gles { +    include!(concat!(env!("OUT_DIR"), "/gles2_bindings.rs")); +} + +#[link(name = "UIKit", kind = "framework")] +#[link(name = "CoreFoundation", kind = "framework")] +#[link(name = "GlKit", kind = "framework")] +extern { +    pub static kCFRunLoopDefaultMode: CFStringRef; + +    pub static kEAGLColorFormatRGB565: id; +    // pub static kEAGLColorFormatRGBA8: id; +    pub static kEAGLDrawablePropertyColorFormat: id; +    pub static kEAGLDrawablePropertyRetainedBacking: id; + +    // int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName ); +    pub fn UIApplicationMain(argc: libc::c_int, argv: *const libc::c_char, principalClassName: id, delegateClassName: id) -> libc::c_int; + +    // SInt32 CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled ); +    pub fn CFRunLoopRunInMode(mode: CFStringRef, seconds: CFTimeInterval, returnAfterSourceHandled: Boolean) -> i32; +} + +extern { +    pub fn setjmp(env: *mut libc::c_void) -> libc::c_int; +    pub fn longjmp(env: *mut libc::c_void, val: libc::c_int); +} + +pub const RTLD_LAZY: libc::c_int = 0x001; +pub const RTLD_GLOBAL: libc::c_int = 0x100; + +extern { +    pub fn dlopen(filename: *const libc::c_char, flag: libc::c_int) -> *mut libc::c_void; +    pub fn dlsym(handle: *mut libc::c_void, symbol: *const libc::c_char) -> *mut libc::c_void; +} + +pub trait NSString { +    unsafe fn alloc(_: Self) -> id { +        msg_send![class("NSString"), alloc] +    } + +    #[allow(non_snake_case)] +    unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id; +    #[allow(non_snake_case)] +    unsafe fn stringByAppendingString_(self, other: id) -> id; +    unsafe fn init_str(self, string: &str) -> Self; +    #[allow(non_snake_case)] +    unsafe fn UTF8String(self) -> *const libc::c_char; +} + +impl NSString for id { +    unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id { +        msg_send![self, initWithUTF8String:c_string as id] +    } + +    unsafe fn stringByAppendingString_(self, other: id) -> id { +        msg_send![self, stringByAppendingString:other] +    } + +    unsafe fn init_str(self, string: &str) -> id { +        let cstring = CString::new(string).unwrap(); +        self.initWithUTF8String_(cstring.as_ptr()) +    } + +    unsafe fn UTF8String(self) -> *const libc::c_char { +        msg_send![self, UTF8String] +    } +} + +#[inline] +pub fn class(name: &str) -> *mut Class { +    unsafe { +        ::std::mem::transmute(Class::get(name)) +    } +} diff --git a/src/api/ios/mod.rs b/src/api/ios/mod.rs new file mode 100644 index 0000000..9d1b527 --- /dev/null +++ b/src/api/ios/mod.rs @@ -0,0 +1,420 @@ +//! iOS support +//! +//! # Building app +//! To build ios app you will need rustc built for this targets: +//! +//!  - armv7-apple-ios +//!  - armv7s-apple-ios +//!  - i386-apple-ios +//!  - aarch64-apple-ios +//!  - x86_64-apple-ios +//! +//! Then +//! +//! ``` +//! cargo build --target=... +//! ``` +//! The simplest way to integrate your app into xcode environment is to build it +//! as a static library. Wrap your main function and export it. +//! +//! ```rust, ignore +//! #[no_mangle] +//! pub extern fn start_glutin_app() { +//!     start_inner() +//! } +//! +//! fn start_inner() { +//!    ... +//! } +//! +//! ``` +//! +//! Compile project and then drag resulting .a into Xcode project. Add glutin.h to xcode. +//! +//! ```c +//! void start_glutin_app(); +//! ``` +//! +//! Use start_glutin_app inside your xcode's main function. +//! +//! +//! # App lifecycle and events +//! +//! iOS environment is very different from other platforms and you must be very +//! careful with it's events. Familiarize yourself with [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/). +//! +//! +//! This is how those event are represented in glutin: +//! +//!  - applicationDidBecomeActive is Focused(true) +//!  - applicationWillResignActive is Focused(false) +//!  - applicationDidEnterBackground is Suspended(true) +//!  - applicationWillEnterForeground is Suspended(false) +//!  - applicationWillTerminate is Closed +//! +//! Keep in mind that after Closed event is received every attempt to draw with opengl will result in segfault. +//! +//! Also note that app will not receive Closed event if suspended, it will be SIGKILL'ed + + + + +#![cfg(target_os = "ios")] +#![deny(warnings)] + +use std::collections::VecDeque; +use std::ptr; +use std::mem; +use std::ffi::CString; + +use libc; +use objc::runtime::{Class, BOOL, YES, NO }; + +use native_monitor::NativeMonitorId; +use { Api, PixelFormat, CreationError, BuilderAttribs, GlContext, CursorState, MouseCursor, Event }; +use CreationError::OsError; + +mod delegate; +use self::delegate::{ create_delegate_class, create_view_class }; + +mod ffi; +use self::ffi::{ +    gles, +    setjmp, +    dlopen, +    dlsym, +    UIApplicationMain, +    kEAGLColorFormatRGB565, +    CFTimeInterval, +    CFRunLoopRunInMode, +    kCFRunLoopDefaultMode, +    kCFRunLoopRunHandledSource, +    kEAGLDrawablePropertyRetainedBacking, +    kEAGLDrawablePropertyColorFormat, +    RTLD_LAZY, +    RTLD_GLOBAL, +    id, +    nil, +    NSString, +    CGFloat + }; + + +static mut jmpbuf: [libc::c_int;27] = [0;27]; + +pub struct MonitorID; + +pub struct Window { +    eagl_context: id, +    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>, +    window: id, +    controller: id, +    view: id, +    size: (u32,u32), +    scale: f32 +} + + +impl DelegateState { +    fn new(window: id, controller:id, view: id, size: (u32,u32), scale: f32) -> DelegateState { +        DelegateState { +            events_queue: VecDeque::new(), +            window: window, +            controller: controller, +            view: view, +            size: size, +            scale: scale +        } +    } +} + + +pub fn get_available_monitors() -> VecDeque<MonitorID> { +    let mut rb = VecDeque::new(); +    rb.push_back(MonitorID); +    rb +} + +pub fn get_primary_monitor() -> MonitorID { +    MonitorID +} + +impl MonitorID { +    pub fn get_name(&self) -> Option<String> { +        Some("Primary".to_string()) +    } + +    pub fn get_native_identifier(&self) -> NativeMonitorId { +        NativeMonitorId::Unavailable +    } + +    pub fn get_dimensions(&self) -> (u32, u32) { +        unimplemented!() +    } +} + + +impl Window { + +    pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> { +        unsafe { +            if setjmp(mem::transmute(&mut jmpbuf)) != 0 { +                let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication]; +                let delegate: id = msg_send![app, delegate]; +                let state: *mut libc::c_void = *(&*delegate).get_ivar("glutinState"); +                let state = state as *mut DelegateState; + +                let context = Window::create_context(); + +                let mut window = Window { +                    eagl_context: context, +                    delegate_state: state +                }; + +                window.init_context(builder); + +                return Ok(window) +            } +        } + +        create_delegate_class(); +        create_view_class(); +        Window::start_app(); + +        Err(CreationError::OsError(format!("Couldn't create UIApplication"))) +    } + +    unsafe fn init_context(&mut self, builder: BuilderAttribs) { +        let draw_props: id = msg_send![Class::get("NSDictionary").unwrap(), alloc]; +            let draw_props: id = msg_send![draw_props, +                    initWithObjects: +                        vec![ +                            msg_send![Class::get("NSNumber").unwrap(), numberWithBool: NO], +                            kEAGLColorFormatRGB565 +                        ].as_ptr() +                    forKeys: +                        vec![ +                            kEAGLDrawablePropertyRetainedBacking, +                            kEAGLDrawablePropertyColorFormat +                        ].as_ptr() +                    count: 2 +            ]; +        self.make_current(); + +        let state = &mut *self.delegate_state; + +        if builder.multitouch { +            let _: () = msg_send![state.view, setMultipleTouchEnabled:YES]; +        } + +        let _: () = msg_send![state.view, setContentScaleFactor:state.scale as CGFloat]; + +        let layer: id = msg_send![state.view, layer]; +        let _: () = msg_send![layer, setContentsScale:state.scale as CGFloat]; +        let _: () = msg_send![layer, setDrawableProperties: draw_props]; + +        let gl = gles::Gles2::load_with(|symbol| self.get_proc_address(symbol)); +        let mut color_render_buf: gles::types::GLuint = 0; +        let mut frame_buf: gles::types::GLuint = 0; +        gl.GenRenderbuffers(1, &mut color_render_buf); +        gl.BindRenderbuffer(gles::RENDERBUFFER, color_render_buf); + +        let ok: BOOL = msg_send![self.eagl_context, renderbufferStorage:gles::RENDERBUFFER fromDrawable:layer]; +        if ok != YES { +            panic!("EAGL: could not set renderbufferStorage"); +        } + +        gl.GenFramebuffers(1, &mut frame_buf); +        gl.BindFramebuffer(gles::FRAMEBUFFER, frame_buf); + +        gl.FramebufferRenderbuffer(gles::FRAMEBUFFER, gles::COLOR_ATTACHMENT0, gles::RENDERBUFFER, color_render_buf); + +        let status = gl.CheckFramebufferStatus(gles::FRAMEBUFFER); +        if gl.CheckFramebufferStatus(gles::FRAMEBUFFER) != gles::FRAMEBUFFER_COMPLETE { +            panic!("framebuffer status: {:?}", status); +        } +    } + +    fn create_context() -> id { +        unsafe { +            let eagl_context: id = msg_send![Class::get("EAGLContext").unwrap(), alloc]; +            let eagl_context: id = msg_send![eagl_context, initWithAPI:2]; // es2 +            eagl_context +        } +    } + +    fn start_app() { +        unsafe { +            UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate")); +        } +    } + +    pub fn is_closed(&self) -> bool { +        false +    } + +    pub fn set_title(&self, _: &str) { +    } + +    pub fn show(&self) { +    } + +    pub fn hide(&self) { +    } + +    pub fn get_position(&self) -> Option<(i32, i32)> { +        None +    } + +    pub fn set_position(&self, _x: i32, _y: i32) { +    } + +    pub fn get_inner_size(&self) -> Option<(u32, u32)> { +        unsafe { Some((&*self.delegate_state).size) } +    } + +    pub fn get_outer_size(&self) -> Option<(u32, u32)> { +        self.get_inner_size() +    } + +    pub fn set_inner_size(&self, _x: u32, _y: u32) { +    } + +    pub fn poll_events(&self) -> PollEventsIterator { +        PollEventsIterator { +            window: self +        } +    } + +    pub fn wait_events(&self) -> WaitEventsIterator { +        WaitEventsIterator { +            window: self +        } +    } + +    pub fn platform_display(&self) -> *mut libc::c_void { +        unimplemented!(); +    } + +    pub fn platform_window(&self) -> *mut libc::c_void { +        unimplemented!() +    } + +    pub fn get_pixel_format(&self) -> PixelFormat { +        unimplemented!(); +    } + +    pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { +    } + +    pub fn set_cursor(&self, _: MouseCursor) { +    } + +    pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> { +        Ok(()) +    } + +    pub fn hidpi_factor(&self) -> f32 { +        unsafe { (&*self.delegate_state) }.scale +    } + +    pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> { +        unimplemented!(); +    } + +    pub fn create_window_proxy(&self) -> WindowProxy { +        WindowProxy +    } + +} + +impl GlContext for Window { +    unsafe fn make_current(&self) { +        let _:BOOL = msg_send![Class::get("EAGLContext").unwrap(), setCurrentContext: self.eagl_context]; +    } + +    fn is_current(&self) -> bool { +        false +    } + +    fn get_proc_address(&self, addr: &str) -> *const libc::c_void { +        let addr_c = CString::new(addr).unwrap(); +        let path = CString::new("/System/Library/Frameworks/OpenGLES.framework/OpenGLES").unwrap(); +        unsafe { +            let lib = dlopen(path.as_ptr(), RTLD_LAZY | RTLD_GLOBAL); +            dlsym(lib, addr_c.as_ptr()) +        } +    } + +    fn swap_buffers(&self) { +        unsafe { let _:BOOL = msg_send![self.eagl_context, presentRenderbuffer: gles::RENDERBUFFER]; } +    } + +    fn get_api(&self) -> Api { +        unimplemented!() +    } + +    fn get_pixel_format(&self) -> PixelFormat { +        unimplemented!() +    } +} + +impl WindowProxy { +    pub fn wakeup_event_loop(&self) { +        unimplemented!() +    } +} + + +impl<'a> Iterator for WaitEventsIterator<'a> { +    type Item = Event; + +    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> { +        unsafe { +            let state = &mut *self.window.delegate_state; + +            if let Some(event) = state.events_queue.pop_front() { +                return Some(event) +            } + +            // 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() +            } + +            // run runloop +            let seconds: CFTimeInterval = 0.000002; +            while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {} + +            state.events_queue.pop_front() +        } +    } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index db5590a..3697255 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -10,3 +10,4 @@ pub mod wayland;  pub mod wgl;  pub mod win32;  pub mod x11; +pub mod ios; diff --git a/src/api/wgl/mod.rs b/src/api/wgl/mod.rs index cf09ad0..222620c 100644 --- a/src/api/wgl/mod.rs +++ b/src/api/wgl/mod.rs @@ -216,7 +216,7 @@ unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'st  {      let share = share.unwrap_or(ptr::null_mut()); -    let ctxt = if let Some((extra_functions, builder, extensions)) = extra { +    if let Some((extra_functions, builder, extensions)) = extra {          if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context").is_some() {              let mut attributes = Vec::new(); @@ -308,33 +308,32 @@ unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'st              attributes.push(0); -            Some(extra_functions.CreateContextAttribsARB(hdc as *const libc::c_void, -                                                         share as *const libc::c_void, -                                                         attributes.as_ptr())) +            let ctxt = extra_functions.CreateContextAttribsARB(hdc as *const libc::c_void, +                                                               share as *const libc::c_void, +                                                               attributes.as_ptr()); -        } else { -            None -        } -    } else { -        None -    }; - -    let ctxt = match ctxt { -        Some(ctxt) => ctxt, -        None => { -            let ctxt = gl::wgl::CreateContext(hdc as *const libc::c_void); -            if !ctxt.is_null() && !share.is_null() { -                gl::wgl::ShareLists(share as *const libc::c_void, ctxt); -            }; -            ctxt +            if ctxt.is_null() { +                return Err(CreationError::OsError(format!("wglCreateContextAttribsARB failed: {}", +                                                      format!("{}", io::Error::last_os_error())))); +            } else { +                return Ok(ContextWrapper(ctxt as winapi::HGLRC)); +            }          }      }; +    let ctxt = gl::wgl::CreateContext(hdc as *const libc::c_void);      if ctxt.is_null() { -        return Err(CreationError::OsError(format!("OpenGL context creation failed: {}", +        return Err(CreationError::OsError(format!("wglCreateContext failed: {}",                                                    format!("{}", io::Error::last_os_error()))));      } +    if !share.is_null() { +        if gl::wgl::ShareLists(share as *const libc::c_void, ctxt) == 0 { +            return Err(CreationError::OsError(format!("wglShareLists failed: {}", +                                                      format!("{}", io::Error::last_os_error())))); +        } +    }; +      Ok(ContextWrapper(ctxt as winapi::HGLRC))  } diff --git a/src/api/win32/callback.rs b/src/api/win32/callback.rs index 6ac56f3..25beb7b 100644 --- a/src/api/win32/callback.rs +++ b/src/api/win32/callback.rs @@ -3,12 +3,15 @@ use std::ptr;  use std::cell::RefCell;  use std::sync::mpsc::Sender;  use std::sync::{Arc, Mutex}; +use std::ffi::OsString; +use std::os::windows::ffi::OsStringExt;  use CursorState;  use Event;  use super::event;  use user32; +use shell32;  use winapi;  /// There's no parameters passed to the callback function, so it needs to get @@ -249,6 +252,25 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,              0          }, +        winapi::WM_DROPFILES => { +            use events::Event::DroppedFile; + +            let hdrop = wparam as winapi::HDROP; +            let mut pathbuf: [u16; winapi::MAX_PATH] = unsafe { mem::uninitialized() }; +            let num_drops = shell32::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0); + +            for i in 0..num_drops { +                let nch = shell32::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(), +                                                  winapi::MAX_PATH as u32) as usize; +                if nch > 0 { +                    send_event(window, DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into())); +                } +            } + +            shell32::DragFinish(hdrop); +            0 +        }, +          _ => {              user32::DefWindowProcW(window, msg, wparam, lparam)          } diff --git a/src/api/win32/init.rs b/src/api/win32/init.rs index 7cb5433..cc6d2d2 100644 --- a/src/api/win32/init.rs +++ b/src/api/win32/init.rs @@ -133,7 +133,8 @@ unsafe fn init(title: Vec<u16>, builder: BuilderAttribs<'static>,              style | winapi::WS_VISIBLE          }; -        let handle = user32::CreateWindowExW(ex_style, class_name.as_ptr(), +        let handle = user32::CreateWindowExW(ex_style | winapi::WS_EX_ACCEPTFILES, +            class_name.as_ptr(),              title.as_ptr() as winapi::LPCWSTR,              style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN,              x.unwrap_or(winapi::CW_USEDEFAULT), y.unwrap_or(winapi::CW_USEDEFAULT), diff --git a/src/events.rs b/src/events.rs index c175810..27ed8b7 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,4 +1,6 @@ -#[derive(Clone, Debug, Copy)] +use std::path::PathBuf; + +#[derive(Clone, Debug)]  pub enum Event {      /// The size of the window has changed.      Resized(u32, u32), @@ -9,6 +11,9 @@ pub enum Event {      /// The window has been closed.      Closed, +    /// A file has been dropped into the window. +    DroppedFile(PathBuf), +      /// The window received a unicode character.      ReceivedCharacter(char), @@ -36,6 +41,46 @@ pub enum Event {      /// The window needs to be redrawn.      Refresh, + +    /// App has been suspended or resumed. +    /// +    /// The parameter is true if app was suspended, and false if it has been resumed. +    Suspended(bool), + + +    /// Touch event has been received +    Touch(Touch) +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum TouchPhase { +    Started, +    Moved, +    Ended, +    Cancelled +} + +#[derive(Debug, Clone, Copy)] +/// Represents touch event +/// +/// Every time user touches screen new Start event with some finger id is generated. +/// When the finger is removed from the screen End event with same id is generated. +/// +/// For every id there will be at least 2 events with phases Start and End (or Cancelled). +/// There may be 0 or more Move events. +/// +/// +/// Depending on platform implementation id may or may not be reused by system after End event. +/// +/// Gesture regonizer using this event should assume that Start event received with same id +/// as previously received End event is a new finger and has nothing to do with an old one. +/// +/// Touch may be cancelled if for example window lost focus. +pub struct Touch { +    pub phase: TouchPhase, +    pub location: (f64,f64), +    /// unique identifier of a finger. +    pub id: u64  }  pub type ScanCode = u8; @@ -54,7 +99,7 @@ pub enum MouseButton {      Other(u8),  } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)]  pub enum MouseScrollDelta {  	/// Amount in lines or rows to scroll in the horizontal  	/// and vertical directions. @@ -219,9 +264,9 @@ pub enum VirtualKeyCode {      NumpadEquals,      OEM102,      Period, -    Playpause, +    PlayPause,      Power, -    Prevtrack, +    PrevTrack,      RAlt,      RBracket,      RControl, @@ -240,7 +285,7 @@ pub enum VirtualKeyCode {      VolumeDown,      VolumeUp,      Wake, -    Webback, +    WebBack,      WebFavorites,      WebForward,      WebHome, @@ -37,12 +37,14 @@ extern crate winapi;  #[cfg(target_os = "windows")]  extern crate kernel32;  #[cfg(target_os = "windows")] +extern crate shell32; +#[cfg(target_os = "windows")]  extern crate gdi32;  #[cfg(target_os = "windows")]  extern crate user32;  #[cfg(target_os = "windows")]  extern crate dwmapi; -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "ios"))]  #[macro_use]  extern crate objc;  #[cfg(target_os = "macos")] @@ -345,6 +347,7 @@ pub struct BuilderAttribs<'a> {      srgb: Option<bool>,      transparent: bool,      decorations: bool, +    multitouch: bool  }  impl BuilderAttribs<'static> { @@ -371,6 +374,7 @@ impl BuilderAttribs<'static> {              srgb: None,              transparent: false,              decorations: true, +            multitouch: false          }      }  } @@ -402,6 +406,7 @@ impl<'a> BuilderAttribs<'a> {              srgb: self.srgb,              transparent: self.transparent,              decorations: self.decorations, +            multitouch: self.multitouch          };          (new_attribs, sharing) diff --git a/src/platform/ios/mod.rs b/src/platform/ios/mod.rs new file mode 100644 index 0000000..7cbdd84 --- /dev/null +++ b/src/platform/ios/mod.rs @@ -0,0 +1,47 @@ +#![cfg(target_os = "ios")] +use libc::c_void; + +use BuilderAttribs; +use CreationError; +use PixelFormat; + +pub use api::ios::*; + +pub struct HeadlessContext(i32); + +impl HeadlessContext { +    /// See the docs in the crate root file. +    pub fn new(_builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> { +        unimplemented!() +    } + +    /// See the docs in the crate root file. +    pub unsafe fn make_current(&self) { +        unimplemented!() +    } + +    pub fn swap_buffers(&self) { +        unimplemented!() +    } + +    /// See the docs in the crate root file. +    pub fn is_current(&self) -> bool { +        unimplemented!() +    } + +    /// See the docs in the crate root file. +    pub fn get_proc_address(&self, _addr: &str) -> *const c_void { +        unimplemented!() +    } + +    pub fn get_api(&self) -> ::Api { +        ::Api::OpenGlEs +    } + +    pub fn get_pixel_format(&self) -> PixelFormat { +        unimplemented!(); +    } +} + +unsafe impl Send for HeadlessContext {} +unsafe impl Sync for HeadlessContext {} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 68ddfcc..c4b2265 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -12,6 +12,9 @@ mod platform;  #[cfg(target_os = "android")]  #[path="android/mod.rs"]  mod platform; +#[cfg(target_os = "ios")] +#[path="ios/mod.rs"] +mod platform; -#[cfg(all(not(target_os = "windows"), not(target_os = "linux"), not(target_os = "macos"), not(target_os = "android")))] +#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"), not(target_os = "macos"), not(target_os = "android")))]  use this_platform_is_not_supported; diff --git a/src/window.rs b/src/window.rs index 9685287..8757efc 100644 --- a/src/window.rs +++ b/src/window.rs @@ -157,6 +157,12 @@ impl<'a> WindowBuilder<'a> {          self      } +    /// Enables multitouch +    pub fn with_multitouch(mut self) -> WindowBuilder<'a> { +        self.attribs.multitouch = true; +        self +    } +      /// Builds the window.      ///      /// Error should be very rare and only occur in case of permission denied, incompatible system,  | 
