aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml28
-rw-r--r--appveyor.yml30
-rw-r--r--build.rs32
-rw-r--r--src/api/cocoa/mod.rs8
-rw-r--r--src/api/egl/mod.rs97
-rw-r--r--src/api/glx/mod.rs82
-rw-r--r--src/api/ios/delegate.rs226
-rw-r--r--src/api/ios/ffi.rs130
-rw-r--r--src/api/ios/mod.rs420
-rw-r--r--src/api/mod.rs1
-rw-r--r--src/api/osmesa/mod.rs8
-rw-r--r--src/api/wayland/context.rs34
-rw-r--r--src/api/wayland/mod.rs4
-rw-r--r--src/api/wgl/mod.rs53
-rw-r--r--src/api/x11/window.rs46
-rw-r--r--src/events.rs40
-rw-r--r--src/headless.rs7
-rw-r--r--src/lib.rs53
-rw-r--r--src/platform/ios/mod.rs47
-rw-r--r--src/platform/mod.rs5
-rw-r--r--src/window.rs13
21 files changed, 1264 insertions, 100 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 14d7faa..131510e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "glutin"
-version = "0.2.0"
+version = "0.2.2"
authors = ["tomaka <pierre.krieger1708@gmail.com>"]
description = "Cross-plaform OpenGL context provider."
keywords = ["windowing", "opengl"]
@@ -22,7 +22,7 @@ shared_library = "0.1.0"
[build-dependencies]
gl_generator = "0.0.26"
-khronos_api = "0.0.5"
+khronos_api = "0.0.7"
[dev-dependencies]
clock_ticks = "0.0.5"
@@ -30,6 +30,18 @@ clock_ticks = "0.0.5"
[target.arm-linux-androideabi.dependencies.android_glue]
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]
objc = "0.1"
cgl = "0"
@@ -63,18 +75,18 @@ dwmapi-sys = "0.1"
[target.i686-unknown-linux-gnu.dependencies]
osmesa-sys = "0.0.5"
-wayland-client = "0.1.6"
-wayland-kbd = "0.1.1"
+wayland-client = { version = "0.2.0", features = ["egl", "dlopen"] }
+wayland-kbd = "0.2.0"
x11-dl = "=1.0.1"
[target.x86_64-unknown-linux-gnu.dependencies]
osmesa-sys = "0.0.5"
-wayland-client = "0.1.6"
-wayland-kbd = "0.1.1"
+wayland-client = { version = "0.2.0", features = ["egl", "dlopen"] }
+wayland-kbd = "0.2.0"
x11-dl = "=1.0.1"
[target.arm-unknown-linux-gnueabihf.dependencies]
osmesa-sys = "0.0.5"
-wayland-client = "0.1.6"
-wayland-kbd = "0.1.1"
+wayland-client = { version = "0.2.0", features = ["egl", "dlopen"] }
+wayland-kbd = "0.2.0"
x11-dl = "=1.0.1"
diff --git a/appveyor.yml b/appveyor.yml
index 17bad94..b3ec520 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,19 +1,21 @@
+environment:
+ CARGO_TARGET: x86_64-pc-windows-gnu
+ matrix:
+ - TARGET: x86_64-pc-windows-msvc
+ - TARGET: i686-pc-windows-gnu
install:
- - ps: |
- if ($env:PLATFORM -eq "x86") {
- Start-FileDownload 'https://static.rust-lang.org/dist/rust-1.0.0-i686-pc-windows-gnu.exe' -FileName rust.exe
- } else {
- Start-FileDownload 'https://static.rust-lang.org/dist/rust-1.0.0-x86_64-pc-windows-gnu.exe' -FileName rust.exe
- }
- - rust.exe /VERYSILENT /NORESTART /DIR="C:\Program Files\Rust"
- - SET PATH=%PATH%;C:\Program Files\Rust\bin
- - rustc -vV
+ - ps: Start-FileDownload "https://static.rust-lang.org/dist/rustc-nightly-${env:TARGET}.tar.gz"
+ - ps: Start-FileDownload "https://static.rust-lang.org/cargo-dist/cargo-nightly-${env:CARGO_TARGET}.tar.gz"
+ - 7z x rustc-nightly-%TARGET%.tar.gz > nul
+ - 7z x rustc-nightly-%TARGET%.tar > nul
+ - 7z x cargo-nightly-%CARGO_TARGET%.tar.gz > nul
+ - 7z x cargo-nightly-%CARGO_TARGET%.tar > nul
+ - call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64
+ - set PATH=%PATH%;%cd%/rustc-nightly-%TARGET%/rustc/bin
+ - set PATH=%PATH%;%cd%/cargo-nightly-%CARGO_TARGET%/cargo/bin
+ - SET PATH=%PATH%;C:\MinGW\bin
+ - rustc -V
- cargo -V
- - git submodule update --init --recursive
-
-platform:
- - x86
- - x64
build: false
diff --git a/build.rs b/build.rs
index c21f4f6..c73f687 100644
--- a/build.rs
+++ b/build.rs
@@ -25,6 +25,7 @@ fn main() {
vec![
"WGL_ARB_create_context".to_string(),
"WGL_ARB_create_context_profile".to_string(),
+ "WGL_ARB_create_context_robustness".to_string(),
"WGL_ARB_extensions_string".to_string(),
"WGL_ARB_framebuffer_sRGB".to_string(),
"WGL_ARB_multisample".to_string(),
@@ -42,7 +43,9 @@ fn main() {
gl_generator::Fallbacks::All,
khronos_api::EGL_XML,
vec![
- "EGL_KHR_create_context".to_string()
+ "EGL_KHR_create_context".to_string(),
+ "EGL_EXT_create_context_robustness".to_string(),
+ "EGL_KHR_create_context_no_error".to_string(),
],
"1.5", "core", &mut file).unwrap();
}
@@ -63,6 +66,7 @@ fn main() {
vec![
"GLX_ARB_create_context".to_string(),
"GLX_ARB_create_context_profile".to_string(),
+ "GLX_ARB_create_context_robustness".to_string(),
"GLX_ARB_framebuffer_sRGB".to_string(),
"GLX_EXT_framebuffer_sRGB".to_string(),
"GLX_EXT_swap_control".to_string(),
@@ -76,7 +80,9 @@ fn main() {
gl_generator::Fallbacks::All,
khronos_api::EGL_XML,
vec![
- "EGL_KHR_create_context".to_string()
+ "EGL_KHR_create_context".to_string(),
+ "EGL_EXT_create_context_robustness".to_string(),
+ "EGL_KHR_create_context_no_error".to_string(),
],
"1.5", "core", &mut file).unwrap();
}
@@ -88,9 +94,31 @@ fn main() {
gl_generator::Fallbacks::All,
khronos_api::EGL_XML,
vec![
+ "EGL_KHR_create_context".to_string(),
+ "EGL_EXT_create_context_robustness".to_string(),
+ "EGL_KHR_create_context_no_error".to_string(),
+ ],
+ "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") {
diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs
index 772b906..332244e 100644
--- a/src/api/cocoa/mod.rs
+++ b/src/api/cocoa/mod.rs
@@ -13,6 +13,7 @@ use GlContext;
use GlProfile;
use GlRequest;
use PixelFormat;
+use Robustness;
use native_monitor::NativeMonitorId;
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
@@ -332,6 +333,13 @@ impl Window {
unimplemented!()
}
+ match builder.gl_robustness {
+ Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
+ return Err(CreationError::NotSupported);
+ },
+ _ => ()
+ }
+
let app = match Window::create_app() {
Some(app) => app,
None => { return Err(OsError(format!("Couldn't create NSApplication"))); },
diff --git a/src/api/egl/mod.rs b/src/api/egl/mod.rs
index 7b31017..1a6cc06 100644
--- a/src/api/egl/mod.rs
+++ b/src/api/egl/mod.rs
@@ -7,6 +7,7 @@ use CreationError;
use GlContext;
use GlRequest;
use PixelFormat;
+use Robustness;
use Api;
use libc;
@@ -116,15 +117,16 @@ impl Context {
let context = unsafe {
if let Some(version) = version {
try!(create_context(&egl, display, &egl_version, api, version, config_id,
- builder.gl_debug).map_err(|_| CreationError::NotSupported))
+ builder.gl_debug, builder.gl_robustness))
} else if api == Api::OpenGlEs {
if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (2, 0),
- config_id, builder.gl_debug)
+ config_id, builder.gl_debug, builder.gl_robustness)
{
ctxt
} else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (1, 0),
- config_id, builder.gl_debug)
+ config_id, builder.gl_debug,
+ builder.gl_robustness)
{
ctxt
} else {
@@ -133,15 +135,17 @@ impl Context {
} else {
if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (3, 2),
- config_id, builder.gl_debug)
+ config_id, builder.gl_debug, builder.gl_robustness)
{
ctxt
} else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (3, 1),
- config_id, builder.gl_debug)
+ config_id, builder.gl_debug,
+ builder.gl_robustness)
{
ctxt
} else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (1, 0),
- config_id, builder.gl_debug)
+ config_id, builder.gl_debug,
+ builder.gl_robustness)
{
ctxt
} else {
@@ -335,8 +339,8 @@ unsafe fn enumerate_configs(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDi
unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay,
egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
api: Api, version: (u8, u8), config_id: ffi::egl::types::EGLConfig,
- gl_debug: bool)
- -> Result<ffi::egl::types::EGLContext, ()>
+ gl_debug: bool, gl_robustness: Robustness)
+ -> Result<ffi::egl::types::EGLContext, CreationError>
{
let extensions = if egl_version >= &(1, 2) {
let p = CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32));
@@ -345,7 +349,8 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl
format!("")
};
- let mut context_attributes = vec![];
+ let mut context_attributes = Vec::with_capacity(10);
+ let mut flags = 0;
if egl_version >= &(1, 5) ||
extensions.contains("EGL_KHR_create_context ") ||
@@ -356,17 +361,85 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
context_attributes.push(version.1 as i32);
+ // handling robustness
+ let supports_robustness = egl_version >= &(1, 5) ||
+ extensions.contains("EGL_EXT_create_context_robustness ") ||
+ extensions.ends_with("EGL_EXT_create_context_robustness");
+
+ match gl_robustness {
+ Robustness::NotRobust => (),
+
+ Robustness::NoError => {
+ if extensions.contains("EGL_KHR_create_context_no_error ") ||
+ extensions.ends_with("EGL_KHR_create_context_no_error")
+ {
+ context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as libc::c_int);
+ context_attributes.push(1);
+ }
+ },
+
+ Robustness::RobustNoResetNotification => {
+ if supports_robustness {
+ context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
+ as libc::c_int);
+ context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as libc::c_int);
+ flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int;
+ } else {
+ return Err(CreationError::NotSupported);
+ }
+ },
+
+ Robustness::TryRobustNoResetNotification => {
+ if supports_robustness {
+ context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
+ as libc::c_int);
+ context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as libc::c_int);
+ flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int;
+ }
+ },
+
+ Robustness::RobustLoseContextOnReset => {
+ if supports_robustness {
+ context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
+ as libc::c_int);
+ context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as libc::c_int);
+ flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int;
+ } else {
+ return Err(CreationError::NotSupported);
+ }
+ },
+
+ Robustness::TryRobustLoseContextOnReset => {
+ if supports_robustness {
+ context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
+ as libc::c_int);
+ context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as libc::c_int);
+ flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int;
+ }
+ },
+ }
+
if gl_debug {
if egl_version >= &(1, 5) {
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
context_attributes.push(ffi::egl::TRUE as i32);
} else {
- context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
- context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32);
+ flags = flags | ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32;
}
}
+ context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
+ context_attributes.push(flags);
+
} else if egl_version >= &(1, 3) && api == Api::OpenGlEs {
+ // robustness is not supported
+ match gl_robustness {
+ Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
+ return Err(CreationError::NotSupported);
+ },
+ _ => ()
+ }
+
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
context_attributes.push(version.0 as i32);
}
@@ -377,7 +450,7 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl
context_attributes.as_ptr());
if context.is_null() {
- return Err(());
+ return Err(CreationError::NotSupported);
}
Ok(context)
diff --git a/src/api/glx/mod.rs b/src/api/glx/mod.rs
index 455f6a5..d286813 100644
--- a/src/api/glx/mod.rs
+++ b/src/api/glx/mod.rs
@@ -8,9 +8,10 @@ use GlProfile;
use GlRequest;
use Api;
use PixelFormat;
+use Robustness;
use libc;
-use std::ffi::CString;
+use std::ffi::{CStr, CString};
use std::{mem, ptr};
use api::x11::ffi;
@@ -48,6 +49,13 @@ impl Context {
ptr::null()
};
+ // loading the list of extensions
+ let extensions = unsafe {
+ let extensions = glx.QueryExtensionsString(display as *mut _, 0); // FIXME: screen number
+ let extensions = CStr::from_ptr(extensions).to_bytes().to_vec();
+ String::from_utf8(extensions).unwrap()
+ };
+
// loading the extra GLX functions
let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {
with_c_str(addr, |s| {
@@ -58,32 +66,35 @@ impl Context {
// creating GL context
let context = match builder.gl_version {
GlRequest::Latest => {
- if let Ok(ctxt) = create_context(&glx, &extra_functions, (3, 2),
- builder.gl_profile, builder.gl_debug, share,
+ if let Ok(ctxt) = create_context(&glx, &extra_functions, &extensions, (3, 2),
+ builder.gl_profile, builder.gl_debug,
+ builder.gl_robustness, share,
display, fb_config, &mut visual_infos)
{
ctxt
- } else if let Ok(ctxt) = create_context(&glx, &extra_functions, (3, 1),
+ } else if let Ok(ctxt) = create_context(&glx, &extra_functions, &extensions, (3, 1),
builder.gl_profile, builder.gl_debug,
- share, display, fb_config,
- &mut visual_infos)
+ builder.gl_robustness, share, display,
+ fb_config, &mut visual_infos)
{
ctxt
} else {
- try!(create_context(&glx, &extra_functions, (1, 0), builder.gl_profile,
- builder.gl_debug, share, display, fb_config,
- &mut visual_infos))
+ try!(create_context(&glx, &extra_functions, &extensions, (1, 0),
+ builder.gl_profile, builder.gl_debug, builder.gl_robustness,
+ share, display, fb_config, &mut visual_infos))
}
},
GlRequest::Specific(Api::OpenGl, (major, minor)) => {
- try!(create_context(&glx, &extra_functions, (major, minor), builder.gl_profile,
- builder.gl_debug, share, display, fb_config, &mut visual_infos))
+ try!(create_context(&glx, &extra_functions, &extensions, (major, minor),
+ builder.gl_profile, builder.gl_debug, builder.gl_robustness,
+ share, display, fb_config, &mut visual_infos))
},
GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
- try!(create_context(&glx, &extra_functions, (major, minor), builder.gl_profile,
- builder.gl_debug, share, display, fb_config, &mut visual_infos))
+ try!(create_context(&glx, &extra_functions, &extensions, (major, minor),
+ builder.gl_profile, builder.gl_debug, builder.gl_robustness,
+ share, display, fb_config, &mut visual_infos))
},
};
@@ -191,9 +202,9 @@ impl Drop for Context {
}
}
-fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx,
+fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str,
version: (u8, u8), profile: Option<GlProfile>, debug: bool,
- share: ffi::GLXContext, display: *mut ffi::Display,
+ robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display,
fb_config: ffi::glx::types::GLXFBConfig,
visual_infos: &mut ffi::glx::types::XVisualInfo)
-> Result<ffi::GLXContext, CreationError>
@@ -219,10 +230,43 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx,
attributes.push(flag as libc::c_int);
}
- if debug {
- attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int);
- attributes.push(ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int);
- }
+ let flags = {
+ let mut flags = 0;
+
+ // robustness
+ if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context_robustness").is_some() {
+ match robustness {
+ Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => {
+ attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int);
+ attributes.push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as libc::c_int);
+ flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int;
+ },
+ Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => {
+ attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int);
+ attributes.push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as libc::c_int);
+ flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int;
+ },
+ Robustness::NotRobust => (),
+ Robustness::NoError => (),
+ }
+ } else {
+ match robustness {
+ Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
+ return Err(CreationError::NotSupported);
+ },
+ _ => ()
+ }
+ }
+
+ if debug {
+ flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int;
+ }
+
+ flags
+ };
+
+ attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int);
+ attributes.push(flags);
attributes.push(0);
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/osmesa/mod.rs b/src/api/osmesa/mod.rs
index daaba52..db0a1e7 100644
--- a/src/api/osmesa/mod.rs
+++ b/src/api/osmesa/mod.rs
@@ -8,6 +8,7 @@ use ContextError;
use CreationError;
use GlContext;
use PixelFormat;
+use Robustness;
use libc;
use std::{mem, ptr};
use std::ffi::CString;
@@ -38,6 +39,13 @@ impl OsMesaContext {
let dimensions = builder.dimensions.unwrap();
+ match builder.gl_robustness {
+ Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
+ return Err(CreationError::NotSupported.into());
+ },
+ _ => ()
+ }
+
Ok(OsMesaContext {
width: dimensions.0,
height: dimensions.1,
diff --git a/src/api/wayland/context.rs b/src/api/wayland/context.rs
index 3b6c0b8..1af6311 100644
--- a/src/api/wayland/context.rs
+++ b/src/api/wayland/context.rs
@@ -1,6 +1,8 @@
-use super::wayland::core::{Display, Registry, Compositor, Shell, Output, ButtonState,
- Seat, Pointer, default_display, WSurface, SurfaceId, Keyboard,
- KeyState};
+use super::wayland::core::{default_display, Display, Registry};
+use super::wayland::core::compositor::{Compositor, SurfaceId, WSurface};
+use super::wayland::core::output::Output;
+use super::wayland::core::seat::{ButtonState, Seat, Pointer, Keyboard, KeyState};
+use super::wayland::core::shell::Shell;
use super::wayland_kbd::MappedKeyboard;
use super::keyboard::keycode_to_vkey;
@@ -68,11 +70,11 @@ impl WaylandContext {
if let Some(ref mut p) = pointer {
// set the enter/leave callbacks
let current_surface = current_pointer_surface.clone();
- p.set_enter_action(move |_, sid, x, y| {
+ p.set_enter_action(move |_, _, sid, x, y| {
*current_surface.lock().unwrap() = Some(sid);
});
let current_surface = current_pointer_surface.clone();
- p.set_leave_action(move |_, sid| {
+ p.set_leave_action(move |_, _, sid| {
*current_surface.lock().unwrap() = None;
});
// set the events callbacks
@@ -90,7 +92,7 @@ impl WaylandContext {
});
let current_surface = current_pointer_surface.clone();
let event_queues = windows_event_queues.clone();
- p.set_button_action(move |_, sid, b, s| {
+ p.set_button_action(move |_, _, sid, b, s| {
let button = match b {
0x110 => MouseButton::Left,
0x111 => MouseButton::Right,
@@ -98,8 +100,8 @@ impl WaylandContext {
_ => return
};
let state = match s {
- ButtonState::WL_POINTER_BUTTON_STATE_RELEASED => ElementState::Released,
- ButtonState::WL_POINTER_BUTTON_STATE_PRESSED => ElementState::Pressed
+ ButtonState::Released => ElementState::Released,
+ ButtonState::Pressed => ElementState::Pressed
};
// dispatch to the appropriate queue
let sid = *current_surface.lock().unwrap();
@@ -119,11 +121,11 @@ impl WaylandContext {
display.sync_roundtrip();
let current_surface = current_keyboard_surface.clone();
- wkbd.set_enter_action(move |_, sid, _| {
+ wkbd.set_enter_action(move |_, _, sid, _| {
*current_surface.lock().unwrap() = Some(sid);
});
let current_surface = current_keyboard_surface.clone();
- wkbd.set_leave_action(move |_, sid| {
+ wkbd.set_leave_action(move |_, _, sid| {
*current_surface.lock().unwrap() = None;
});
@@ -132,10 +134,10 @@ impl WaylandContext {
// We managed to load a keymap
let current_surface = current_keyboard_surface.clone();
let event_queues = windows_event_queues.clone();
- mkbd.set_key_action(move |state, _, _, keycode, keystate| {
+ mkbd.set_key_action(move |state, _, _, _, keycode, keystate| {
let kstate = match keystate {
- KeyState::WL_KEYBOARD_KEY_STATE_RELEASED => ElementState::Released,
- KeyState::WL_KEYBOARD_KEY_STATE_PRESSED => ElementState::Pressed
+ KeyState::Released => ElementState::Released,
+ KeyState::Pressed => ElementState::Pressed
};
let mut events = Vec::new();
// key event
@@ -167,10 +169,10 @@ impl WaylandContext {
// fallback to raw inputs, no virtual keycodes
let current_surface = current_keyboard_surface.clone();
let event_queues = windows_event_queues.clone();
- rkbd.set_key_action(move |_, _, keycode, keystate| {
+ rkbd.set_key_action(move |_, _, _, keycode, keystate| {
let kstate = match keystate {
- KeyState::WL_KEYBOARD_KEY_STATE_RELEASED => ElementState::Released,
- KeyState::WL_KEYBOARD_KEY_STATE_PRESSED => ElementState::Pressed
+ KeyState::Released => ElementState::Released,
+ KeyState::Pressed => ElementState::Pressed
};
let event = Event::KeyboardInput(kstate, (keycode & 0xff) as u8, None);
// dispatch to the appropriate queue
diff --git a/src/api/wayland/mod.rs b/src/api/wayland/mod.rs
index 852172b..0af5496 100644
--- a/src/api/wayland/mod.rs
+++ b/src/api/wayland/mod.rs
@@ -2,7 +2,9 @@
#![allow(unused_variables, dead_code)]
use self::wayland::egl::{EGLSurface, is_egl_available};
-use self::wayland::core::{ShellSurface, Surface, Output, ShellFullscreenMethod};
+use self::wayland::core::Surface;
+use self::wayland::core::output::Output;
+use self::wayland::core::shell::{ShellSurface, ShellFullscreenMethod};
use libc;
use api::dlopen;
diff --git a/src/api/wgl/mod.rs b/src/api/wgl/mod.rs
index c33054e..cf09ad0 100644
--- a/src/api/wgl/mod.rs
+++ b/src/api/wgl/mod.rs
@@ -7,6 +7,7 @@ use GlContext;
use GlRequest;
use GlProfile;
use PixelFormat;
+use Robustness;
use Api;
use self::make_current_guard::CurrentContextGuard;
@@ -131,9 +132,7 @@ impl Context {
// handling vsync
if builder.vsync {
- // contrary to most extensions, it is permitted to discover the presence of
- // `WGL_EXT_swap_control` by seeing if the function pointer is available
- if extra_functions.SwapIntervalEXT.is_loaded() {
+ if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() {
let _guard = try!(CurrentContextGuard::make_current(hdc, context.0));
if extra_functions.SwapIntervalEXT(1) == 0 {
@@ -181,11 +180,14 @@ impl GlContext for Context {
}
fn swap_buffers(&self) -> Result<(), ContextError> {
- if unsafe { gdi32::SwapBuffers(self.hdc) } != 0 {
+ // TODO: decide how to handle the error
+ /*if unsafe { gdi32::SwapBuffers(self.hdc) } != 0 {
Ok(())
} else {
Err(ContextError::IoError(io::Error::last_os_error()))
- }
+ }*/
+ unsafe { gdi32::SwapBuffers(self.hdc) };
+ Ok(())
}
fn get_api(&self) -> Api {
@@ -266,10 +268,43 @@ unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'st
}
}
- if builder.gl_debug {
- attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int);
- attributes.push(gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int);
- }
+ let flags = {
+ let mut flags = 0;
+
+ // robustness
+ if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_robustness").is_some() {
+ match builder.gl_robustness {
+ Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => {
+ attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int);
+ attributes.push(gl::wgl_extra::NO_RESET_NOTIFICATION_ARB as libc::c_int);
+ flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int;
+ },
+ Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => {
+ attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int);
+ attributes.push(gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as libc::c_int);
+ flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int;
+ },
+ Robustness::NotRobust => (),
+ Robustness::NoError => (),
+ }
+ } else {
+ match builder.gl_robustness {
+ Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
+ return Err(CreationError::NotSupported);
+ },
+ _ => ()
+ }
+ }
+
+ if builder.gl_debug {
+ flags = flags | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int;
+ }
+
+ flags
+ };
+
+ attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int);
+ attributes.push(flags);
attributes.push(0);
diff --git a/src/api/x11/window.rs b/src/api/x11/window.rs
index dc24e38..15e27ce 100644
--- a/src/api/x11/window.rs
+++ b/src/api/x11/window.rs
@@ -365,29 +365,44 @@ impl Window {
preferred_fb
};
- let mut best_mode = -1;
- let modes = unsafe {
+ // finding the mode to switch to if necessary
+ let (mode_to_switch_to, xf86_desk_mode) = unsafe {
let mut mode_num: libc::c_int = mem::uninitialized();
let mut modes: *mut *mut ffi::XF86VidModeModeInfo = mem::uninitialized();
if (display.xf86vmode.XF86VidModeGetAllModeLines)(display.display, screen_id, &mut mode_num, &mut modes) == 0 {
return Err(OsError(format!("Could not query the video modes")));
}
- for i in 0..mode_num {
- let mode: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _);
- if mode.hdisplay == dimensions.0 as u16 && mode.vdisplay == dimensions.1 as u16 {
- best_mode = i;
+ let xf86_desk_mode = *modes.offset(0);
+
+ // FIXME: `XF86VidModeModeInfo` is missing its `hskew` field. Therefore we point to
+ // `vsyncstart` instead of `vdisplay` as a temporary hack.
+
+ let mode_to_switch_to = if builder.monitor.is_some() {
+ let matching_mode = (0 .. mode_num).map(|i| {
+ let m: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _); m
+ }).find(|m| m.hdisplay == dimensions.0 as u16 && m.vsyncstart == dimensions.1 as u16);
+
+ if let Some(matching_mode) = matching_mode {
+ Some(matching_mode)
+
+ } else {
+ let m = (0 .. mode_num).map(|i| {
+ let m: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _); m
+ }).find(|m| m.hdisplay >= dimensions.0 as u16 && m.vsyncstart == dimensions.1 as u16);
+
+ match m {
+ Some(m) => Some(m),
+ None => return Err(OsError(format!("Could not find a suitable graphics mode")))
+ }
}
+ } else {
+ None
};
- if best_mode == -1 && builder.monitor.is_some() {
- return Err(OsError(format!("Could not find a suitable graphics mode")));
- }
- modes
- };
+ (display.xlib.XFree)(modes as *mut _);
- let xf86_desk_mode = unsafe {
- *modes.offset(0)
+ (mode_to_switch_to, xf86_desk_mode)
};
// getting the visual infos
@@ -459,10 +474,11 @@ impl Window {
window_attributes |= ffi::CWBackPixel;
}
- if builder.monitor.is_some() {
+ // switching to fullscreen
+ if let Some(mut mode_to_switch_to) = mode_to_switch_to {
window_attributes |= ffi::CWOverrideRedirect;
unsafe {
- (display.xf86vmode.XF86VidModeSwitchToMode)(display.display, screen_id, *modes.offset(best_mode as isize));
+ (display.xf86vmode.XF86VidModeSwitchToMode)(display.display, screen_id, &mut mode_to_switch_to);
(display.xf86vmode.XF86VidModeSetViewPort)(display.display, screen_id, 0, 0);
set_win_attr.override_redirect = 1;
}
diff --git a/src/events.rs b/src/events.rs
index ef2c61d..42c7595 100644
--- a/src/events.rs
+++ b/src/events.rs
@@ -41,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;
diff --git a/src/headless.rs b/src/headless.rs
index 8ee81d8..69f9aab 100644
--- a/src/headless.rs
+++ b/src/headless.rs
@@ -5,6 +5,7 @@ use CreationError;
use GlRequest;
use GlContext;
use PixelFormat;
+use Robustness;
use gl_common;
use libc;
@@ -43,6 +44,12 @@ impl HeadlessRendererBuilder {
self
}
+ /// Sets the robustness of the OpenGL context. See the docs of `Robustness`.
+ pub fn with_gl_robustness(mut self, robustness: Robustness) -> HeadlessRendererBuilder {
+ self.attribs.gl_robustness = robustness;
+ self
+ }
+
/// Builds the headless context.
///
/// Error should be very rare and only occur in case of permission denied, incompatible system,
diff --git a/src/lib.rs b/src/lib.rs
index 928966f..f128f40 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -44,7 +44,7 @@ extern crate gdi32;
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")]
@@ -199,6 +199,40 @@ impl GlRequest {
/// the compatibility profile features.
pub static GL_CORE: GlRequest = GlRequest::Specific(Api::OpenGl, (3, 2));
+/// Specifies the tolerance of the OpenGL context to faults. If you accept raw OpenGL commands
+/// and/or raw shader code from an untrusted source, you should definitely care about this.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Robustness {
+ /// Not everything is checked. Your application can crash if you do something wrong with your
+ /// shaders.
+ NotRobust,
+
+ /// The driver doesn't check anything. This option is very dangerous. Please know what you're
+ /// doing before using it. See the `GL_KHR_no_error` extension.
+ ///
+ /// Since this option is purely an optimisation, no error will be returned if the backend
+ /// doesn't support it. Instead it will automatically fall back to `NotRobust`.
+ NoError,
+
+ /// Everything is checked to avoid any crash. The driver will attempt to avoid any problem,
+ /// but if a problem occurs the behavior is implementation-defined. You are just guaranteed not
+ /// to get a crash.
+ RobustNoResetNotification,
+
+ /// Same as `RobustNoResetNotification` but the context creation doesn't fail if it's not
+ /// supported.
+ TryRobustNoResetNotification,
+
+ /// Everything is checked to avoid any crash. If a problem occurs, the context will enter a
+ /// "context lost" state. It must then be recreated. For the moment, glutin doesn't provide a
+ /// way to recreate a context with the same window :-/
+ RobustLoseContextOnReset,
+
+ /// Same as `RobustLoseContextOnReset` but the context creation doesn't fail if it's not
+ /// supported.
+ TryRobustLoseContextOnReset,
+}
+
#[derive(Debug, Copy, Clone)]
pub enum MouseCursor {
/// The platform-dependent default cursor.
@@ -301,6 +335,7 @@ pub struct BuilderAttribs<'a> {
gl_version: GlRequest,
gl_profile: Option<GlProfile>,
gl_debug: bool,
+ gl_robustness: Robustness,
vsync: bool,
visible: bool,
multisampling: Option<u16>,
@@ -312,6 +347,7 @@ pub struct BuilderAttribs<'a> {
srgb: Option<bool>,
transparent: bool,
decorations: bool,
+ multitouch: bool
}
impl BuilderAttribs<'static> {
@@ -326,6 +362,7 @@ impl BuilderAttribs<'static> {
gl_version: GlRequest::Latest,
gl_profile: None,
gl_debug: cfg!(debug_assertions),
+ gl_robustness: Robustness::NotRobust,
vsync: false,
visible: true,
multisampling: None,
@@ -337,6 +374,7 @@ impl BuilderAttribs<'static> {
srgb: None,
transparent: false,
decorations: true,
+ multitouch: false
}
}
}
@@ -356,6 +394,7 @@ impl<'a> BuilderAttribs<'a> {
gl_version: self.gl_version,
gl_profile: self.gl_profile,
gl_debug: self.gl_debug,
+ gl_robustness: self.gl_robustness,
vsync: self.vsync,
visible: self.visible,
multisampling: self.multisampling,
@@ -367,6 +406,7 @@ impl<'a> BuilderAttribs<'a> {
srgb: self.srgb,
transparent: self.transparent,
decorations: self.decorations,
+ multitouch: self.multitouch
};
(new_attribs, sharing)
@@ -400,8 +440,15 @@ impl<'a> BuilderAttribs<'a> {
continue;
}
- if self.multisampling.is_some() && format.multisampling.is_none() {
- continue;
+ if let Some(req_ms) = self.multisampling {
+ match format.multisampling {
+ Some(val) if val >= req_ms => (),
+ _ => continue
+ }
+ } else {
+ if format.multisampling.is_some() {
+ continue;
+ }
}
if let Some(srgb) = self.srgb {
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 485ba58..8757efc 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -12,6 +12,7 @@ use GlProfile;
use GlRequest;
use MouseCursor;
use PixelFormat;
+use Robustness;
use native_monitor::NativeMonitorId;
use gl_common;
@@ -84,6 +85,12 @@ impl<'a> WindowBuilder<'a> {
self
}
+ /// Sets the robustness of the OpenGL context. See the docs of `Robustness`.
+ pub fn with_gl_robustness(mut self, robustness: Robustness) -> WindowBuilder<'a> {
+ self.attribs.gl_robustness = robustness;
+ self
+ }
+
/// Requests that the window has vsync enabled.
pub fn with_vsync(mut self) -> WindowBuilder<'a> {
self.attribs.vsync = true;
@@ -150,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,