2015-06-05 16:38:21 +03:00
//! 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 ,
2015-06-08 12:26:42 +03:00
NSString ,
CGFloat
2015-06-05 16:38:21 +03:00
} ;
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 ,
2015-06-08 12:26:42 +03:00
size : ( u32 , u32 ) ,
scale : f32
2015-06-05 16:38:21 +03:00
}
impl DelegateState {
2015-06-08 12:26:42 +03:00
fn new ( window : id , controller :id , view : id , size : ( u32 , u32 ) , scale : f32 ) -> DelegateState {
2015-06-05 16:38:21 +03:00
DelegateState {
events_queue : VecDeque ::new ( ) ,
window : window ,
controller : controller ,
view : view ,
2015-06-08 12:26:42 +03:00
size : size ,
scale : scale
2015-06-05 16:38:21 +03:00
}
}
}
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 ] ;
}
2015-06-08 12:26:42 +03:00
let _ : ( ) = msg_send! [ state . view , setContentScaleFactor :state . scale as CGFloat ] ;
2015-06-05 16:38:21 +03:00
let layer : id = msg_send! [ state . view , layer ] ;
2015-06-08 12:26:42 +03:00
let _ : ( ) = msg_send! [ layer , setContentsScale :state . scale as CGFloat ] ;
2015-06-05 16:38:21 +03:00
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 {
2015-06-08 12:26:42 +03:00
unsafe { ( & * self . delegate_state ) } . scale
2015-06-05 16:38:21 +03:00
}
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 ( )
}
}
}