Merge pull request #477 from rozaliev/feature/ios

[WIP] ios support
This commit is contained in:
tomaka 2015-06-29 09:29:46 +02:00
commit 836e53e55a
11 changed files with 910 additions and 2 deletions

View file

@ -30,6 +30,18 @@ clock_ticks = "0.0.5"
[target.arm-linux-androideabi.dependencies.android_glue] [target.arm-linux-androideabi.dependencies.android_glue]
version = "0" version = "0"
[target.x86_64-apple-ios.dependencies]
objc = "0.1"
[target.aarch64-apple-ios.dependencies]
objc = "0.1"
[target.armv7s-apple-ios.dependencies]
objc = "0.1"
[target.armv7-apple-ios.dependencies]
objc = "0.1"
[target.x86_64-apple-darwin.dependencies] [target.x86_64-apple-darwin.dependencies]
objc = "0.1" objc = "0.1"
cgl = "0" cgl = "0"

View file

@ -101,6 +101,26 @@ fn main() {
"1.5", "core", &mut file).unwrap(); "1.5", "core", &mut file).unwrap();
} }
if target.contains("ios") {
let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
gl_generator::generate_bindings(gl_generator::StaticStructGenerator,
gl_generator::registry::Ns::Egl,
gl_generator::Fallbacks::All,
khronos_api::EGL_XML,
vec![
"EGL_KHR_create_context".to_string()
],
"1.5", "core", &mut file).unwrap();
let mut file = File::create(&dest.join("gles2_bindings.rs")).unwrap();
gl_generator::generate_bindings(gl_generator::StaticStructGenerator,
gl_generator::registry::Ns::Gles2,
gl_generator::Fallbacks::None,
khronos_api::GL_XML,
vec![],
"2.0", "core", &mut file).unwrap();
}
if target.contains("darwin") { if target.contains("darwin") {
let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap(); let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
gl_generator::generate_bindings(gl_generator::GlobalGenerator, gl_generator::generate_bindings(gl_generator::GlobalGenerator,

226
src/api/ios/delegate.rs Normal file
View file

@ -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();
}
}

130
src/api/ios/ffi.rs Normal file
View file

@ -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))
}
}

420
src/api/ios/mod.rs Normal file
View file

@ -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()
}
}
}

View file

@ -10,3 +10,4 @@ pub mod wayland;
pub mod wgl; pub mod wgl;
pub mod win32; pub mod win32;
pub mod x11; pub mod x11;
pub mod ios;

View file

@ -36,6 +36,46 @@ pub enum Event {
/// The window needs to be redrawn. /// The window needs to be redrawn.
Refresh, 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; pub type ScanCode = u8;

View file

@ -42,7 +42,7 @@ extern crate gdi32;
extern crate user32; extern crate user32;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
extern crate dwmapi; extern crate dwmapi;
#[cfg(target_os = "macos")] #[cfg(any(target_os = "macos", target_os = "ios"))]
#[macro_use] #[macro_use]
extern crate objc; extern crate objc;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -345,6 +345,7 @@ pub struct BuilderAttribs<'a> {
srgb: Option<bool>, srgb: Option<bool>,
transparent: bool, transparent: bool,
decorations: bool, decorations: bool,
multitouch: bool
} }
impl BuilderAttribs<'static> { impl BuilderAttribs<'static> {
@ -371,6 +372,7 @@ impl BuilderAttribs<'static> {
srgb: None, srgb: None,
transparent: false, transparent: false,
decorations: true, decorations: true,
multitouch: false
} }
} }
} }
@ -402,6 +404,7 @@ impl<'a> BuilderAttribs<'a> {
srgb: self.srgb, srgb: self.srgb,
transparent: self.transparent, transparent: self.transparent,
decorations: self.decorations, decorations: self.decorations,
multitouch: self.multitouch
}; };
(new_attribs, sharing) (new_attribs, sharing)

47
src/platform/ios/mod.rs Normal file
View file

@ -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 {}

View file

@ -12,6 +12,9 @@ mod platform;
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
#[path="android/mod.rs"] #[path="android/mod.rs"]
mod platform; 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; use this_platform_is_not_supported;

View file

@ -157,6 +157,12 @@ impl<'a> WindowBuilder<'a> {
self self
} }
/// Enables multitouch
pub fn with_multitouch(mut self) -> WindowBuilder<'a> {
self.attribs.multitouch = true;
self
}
/// Builds the window. /// Builds the window.
/// ///
/// Error should be very rare and only occur in case of permission denied, incompatible system, /// Error should be very rare and only occur in case of permission denied, incompatible system,