From 84703027d63e22361197a1fc74a4949becf5fccb Mon Sep 17 00:00:00 2001 From: Evgeny Rozaliev Date: Fri, 5 Jun 2015 16:38:21 +0300 Subject: [add] ios support --- src/api/ios/mod.rs | 415 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 src/api/ios/mod.rs (limited to 'src/api/ios/mod.rs') diff --git a/src/api/ios/mod.rs b/src/api/ios/mod.rs new file mode 100644 index 0000000..fbb0c93 --- /dev/null +++ b/src/api/ios/mod.rs @@ -0,0 +1,415 @@ +//! 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 + }; + + +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, + window: id, + controller: id, + view: id, + size: (u32,u32) +} + + +impl DelegateState { + fn new(window: id, controller:id, view: id, size: (u32,u32)) -> DelegateState { + DelegateState { + events_queue: VecDeque::new(), + window: window, + controller: controller, + view: view, + size: size + } + } +} + + +pub fn get_available_monitors() -> VecDeque { + 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 { + 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 { + 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 layer: id = msg_send![state.view, layer]; + 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) { + } + + pub fn set_cursor(&self, _: MouseCursor) { + } + + pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> { + Ok(()) + } + + pub fn hidpi_factor(&self) -> f32 { + 1.0 + } + + 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 { + 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 { + 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() + } + } +} -- cgit v1.2.3 From 7053837ac19d5c6159f492e177d417aa994b7d04 Mon Sep 17 00:00:00 2001 From: Evgeny Rozaliev Date: Mon, 8 Jun 2015 12:26:42 +0300 Subject: [add] content scale --- src/api/ios/delegate.rs | 5 ++++- src/api/ios/mod.rs | 17 +++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'src/api/ios/mod.rs') diff --git a/src/api/ios/delegate.rs b/src/api/ios/delegate.rs index 1d9f90e..62cd89d 100644 --- a/src/api/ios/delegate.rs +++ b/src/api/ios/delegate.rs @@ -13,6 +13,7 @@ use super::ffi::{ nil, CGRect, CGPoint, + CGFloat, UIViewAutoresizingFlexibleWidth, UIViewAutoresizingFlexibleHeight }; @@ -25,6 +26,8 @@ pub fn create_delegate_class() { 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()]; @@ -47,7 +50,7 @@ pub fn create_delegate_class() { let _: () = msg_send![window, addSubview:view]; let _: () = msg_send![window, makeKeyAndVisible]; - let state = Box::new(DelegateState::new(window, view_controller, view, size)); + 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); diff --git a/src/api/ios/mod.rs b/src/api/ios/mod.rs index fbb0c93..9d1b527 100644 --- a/src/api/ios/mod.rs +++ b/src/api/ios/mod.rs @@ -95,7 +95,8 @@ use self::ffi::{ RTLD_GLOBAL, id, nil, - NSString + NSString, + CGFloat }; @@ -125,18 +126,20 @@ struct DelegateState { window: id, controller: id, view: id, - size: (u32,u32) + size: (u32,u32), + scale: f32 } impl DelegateState { - fn new(window: id, controller:id, view: id, size: (u32,u32)) -> 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 + size: size, + scale: scale } } } @@ -220,7 +223,10 @@ impl Window { 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)); @@ -243,7 +249,6 @@ impl Window { if gl.CheckFramebufferStatus(gles::FRAMEBUFFER) != gles::FRAMEBUFFER_COMPLETE { panic!("framebuffer status: {:?}", status); } - } fn create_context() -> id { @@ -326,7 +331,7 @@ impl Window { } pub fn hidpi_factor(&self) -> f32 { - 1.0 + unsafe { (&*self.delegate_state) }.scale } pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> { -- cgit v1.2.3