diff options
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/android/ffi.rs | 23 | ||||
-rw-r--r-- | src/api/android/mod.rs | 226 | ||||
-rw-r--r-- | src/api/caca/mod.rs | 83 | ||||
-rw-r--r-- | src/api/cocoa/headless.rs | 21 | ||||
-rw-r--r-- | src/api/cocoa/mod.rs | 251 | ||||
-rw-r--r-- | src/api/egl/ffi.rs | 4 | ||||
-rw-r--r-- | src/api/egl/mod.rs | 316 | ||||
-rw-r--r-- | src/api/emscripten/ffi.rs | 82 | ||||
-rw-r--r-- | src/api/emscripten/mod.rs | 251 | ||||
-rw-r--r-- | src/api/glx/mod.rs | 36 | ||||
-rw-r--r-- | src/api/mod.rs | 1 | ||||
-rw-r--r-- | src/api/osmesa/mod.rs | 48 | ||||
-rw-r--r-- | src/api/win32/init.rs | 35 | ||||
-rw-r--r-- | src/api/win32/mod.rs | 81 | ||||
-rw-r--r-- | src/api/x11/mod.rs | 97 |
15 files changed, 1017 insertions, 538 deletions
diff --git a/src/api/android/ffi.rs b/src/api/android/ffi.rs index 111f670..1398b77 100644 --- a/src/api/android/ffi.rs +++ b/src/api/android/ffi.rs @@ -5,29 +5,6 @@ use libc; -pub mod egl { - pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t; - pub type khronos_uint64_t = super::khronos_uint64_t; - pub type khronos_ssize_t = super::khronos_ssize_t; - pub type EGLNativeDisplayType = super::EGLNativeDisplayType; - pub type EGLNativePixmapType = super::EGLNativePixmapType; - pub type EGLNativeWindowType = super::EGLNativeWindowType; - pub type EGLint = super::EGLint; - pub type NativeDisplayType = super::EGLNativeDisplayType; - pub type NativePixmapType = super::EGLNativePixmapType; - pub type NativeWindowType = super::EGLNativeWindowType; - - include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); -} - -pub type khronos_utime_nanoseconds_t = khronos_uint64_t; -pub type khronos_uint64_t = libc::uint64_t; -pub type khronos_ssize_t = libc::c_long; -pub type EGLint = libc::int32_t; -pub type EGLNativeDisplayType = *const libc::c_void; -pub type EGLNativePixmapType = *const libc::c_void; // FIXME: egl_native_pixmap_t instead -pub type EGLNativeWindowType = *const ANativeWindow; - #[link(name = "android")] #[link(name = "EGL")] #[link(name = "GLESv2")] diff --git a/src/api/android/mod.rs b/src/api/android/mod.rs index c769fc8..9a98322 100644 --- a/src/api/android/mod.rs +++ b/src/api/android/mod.rs @@ -16,14 +16,16 @@ use std::collections::VecDeque; use Api; use BuilderAttribs; use CursorState; +use GlContext; use GlRequest; use PixelFormat; use native_monitor::NativeMonitorId; +use api::egl; +use api::egl::Context as EglContext; + pub struct Window { - display: ffi::egl::types::EGLDisplay, - context: ffi::egl::types::EGLContext, - surface: ffi::egl::types::EGLSurface, + context: EglContext, event_rx: Receiver<android_glue::Event>, } @@ -31,7 +33,7 @@ pub struct MonitorID; mod ffi; -pub fn get_available_monitors() -> VecDeque <MonitorID> { +pub fn get_available_monitors() -> VecDeque<MonitorID> { let mut rb = VecDeque::new(); rb.push_back(MonitorID); rb @@ -55,41 +57,6 @@ impl MonitorID { } } -#[cfg(feature = "headless")] -pub struct HeadlessContext(i32); - -#[cfg(feature = "headless")] -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!() - } - - /// 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 () { - unimplemented!() - } - - pub fn get_api(&self) -> ::Api { - ::Api::OpenGlEs - } -} - -#[cfg(feature = "headless")] -unsafe impl Send for HeadlessContext {} -#[cfg(feature = "headless")] -unsafe impl Sync for HeadlessContext {} - pub struct PollEventsIterator<'a> { window: &'a Window, } @@ -138,125 +105,19 @@ impl Window { pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> { use std::{mem, ptr}; - if builder.sharing.is_some() { - unimplemented!() - } - let native_window = unsafe { android_glue::get_native_window() }; if native_window.is_null() { return Err(OsError(format!("Android's native window is null"))); } - let display = unsafe { - let display = ffi::egl::GetDisplay(mem::transmute(ffi::egl::DEFAULT_DISPLAY)); - if display.is_null() { - return Err(OsError("No EGL display connection available".to_string())); - } - display - }; - - android_glue::write_log("eglGetDisplay succeeded"); - - let (_major, _minor) = unsafe { - let mut major: ffi::egl::types::EGLint = mem::uninitialized(); - let mut minor: ffi::egl::types::EGLint = mem::uninitialized(); - - if ffi::egl::Initialize(display, &mut major, &mut minor) == 0 { - return Err(OsError(format!("eglInitialize failed"))) - } - - (major, minor) - }; - - android_glue::write_log("eglInitialize succeeded"); - - let use_gles2 = match builder.gl_version { - GlRequest::Specific(Api::OpenGlEs, (2, _)) => true, - GlRequest::Specific(Api::OpenGlEs, _) => false, - GlRequest::Specific(_, _) => panic!("Only OpenGL ES is supported"), // FIXME: return a result - GlRequest::GlThenGles { opengles_version: (2, _), .. } => true, - _ => false, - }; - - let mut attribute_list = vec!(); - - if use_gles2 { - attribute_list.push(ffi::egl::RENDERABLE_TYPE as i32); - attribute_list.push(ffi::egl::OPENGL_ES2_BIT as i32); - } - - { - let (red, green, blue) = match builder.color_bits.unwrap_or(24) { - 24 => (8, 8, 8), - 16 => (6, 5, 6), - _ => panic!("Bad color_bits"), - }; - attribute_list.push(ffi::egl::RED_SIZE as i32); - attribute_list.push(red); - attribute_list.push(ffi::egl::GREEN_SIZE as i32); - attribute_list.push(green); - attribute_list.push(ffi::egl::BLUE_SIZE as i32); - attribute_list.push(blue); - } - - attribute_list.push(ffi::egl::DEPTH_SIZE as i32); - attribute_list.push(builder.depth_bits.unwrap_or(8) as i32); - - attribute_list.push(ffi::egl::NONE as i32); - - let config = unsafe { - let mut num_config: ffi::egl::types::EGLint = mem::uninitialized(); - let mut config: ffi::egl::types::EGLConfig = mem::uninitialized(); - if ffi::egl::ChooseConfig(display, attribute_list.as_ptr(), &mut config, 1, - &mut num_config) == 0 - { - return Err(OsError(format!("eglChooseConfig failed"))) - } - - if num_config <= 0 { - return Err(OsError(format!("eglChooseConfig returned no available config"))) - } - - config - }; - - android_glue::write_log("eglChooseConfig succeeded"); - - let context = unsafe { - let mut context_attributes = vec!(); - if use_gles2 { - context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); - context_attributes.push(2); - } - context_attributes.push(ffi::egl::NONE as i32); - - let context = ffi::egl::CreateContext(display, config, ptr::null(), - context_attributes.as_ptr()); - if context.is_null() { - return Err(OsError(format!("eglCreateContext failed"))) - } - context - }; - - android_glue::write_log("eglCreateContext succeeded"); - - let surface = unsafe { - let surface = ffi::egl::CreateWindowSurface(display, config, native_window, ptr::null()); - if surface.is_null() { - return Err(OsError(format!("eglCreateWindowSurface failed"))) - } - surface - }; - - android_glue::write_log("eglCreateWindowSurface succeeded"); + let context = try!(EglContext::new(egl::ffi::egl::Egl, builder, None, + native_window as *const _)); let (tx, rx) = channel(); android_glue::add_sender(tx); Ok(Window { - display: display, context: context, - surface: surface, event_rx: rx, }) } @@ -317,42 +178,14 @@ impl Window { } } - pub fn make_current(&self) { - unsafe { - ffi::egl::MakeCurrent(self.display, self.surface, self.surface, self.context); - } - } - - pub fn is_current(&self) -> bool { - unsafe { ffi::egl::GetCurrentContext() == self.context } - } - - pub fn get_proc_address(&self, addr: &str) -> *const () { - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - unsafe { - ffi::egl::GetProcAddress(addr) as *const () - } - } - - pub fn swap_buffers(&self) { - unsafe { - ffi::egl::SwapBuffers(self.display, self.surface); - } - } - pub fn platform_display(&self) -> *mut libc::c_void { - self.display as *mut libc::c_void + unimplemented!(); } pub fn platform_window(&self) -> *mut libc::c_void { unimplemented!() } - pub fn get_api(&self) -> ::Api { - ::Api::OpenGlEs - } - pub fn get_pixel_format(&self) -> PixelFormat { unimplemented!(); } @@ -379,6 +212,32 @@ impl Window { unsafe impl Send for Window {} unsafe impl Sync for Window {} +impl GlContext for Window { + unsafe fn make_current(&self) { + self.context.make_current() + } + + fn is_current(&self) -> bool { + self.context.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.context.get_proc_address(addr) + } + + fn swap_buffers(&self) { + self.context.swap_buffers() + } + + fn get_api(&self) -> Api { + self.context.get_api() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.context.get_pixel_format() + } +} + #[cfg(feature = "window")] #[derive(Clone)] pub struct WindowProxy; @@ -388,18 +247,3 @@ impl WindowProxy { unimplemented!() } } - -impl Drop for Window { - fn drop(&mut self) { - use std::ptr; - - unsafe { - // we don't call MakeCurrent(0, 0) because we are not sure that the context - // is still the current one - android_glue::write_log("Destroying gl-init window"); - ffi::egl::DestroySurface(self.display, self.surface); - ffi::egl::DestroyContext(self.display, self.context); - ffi::egl::Terminate(self.display); - } - } -} diff --git a/src/api/caca/mod.rs b/src/api/caca/mod.rs index d8199dd..1e3840c 100644 --- a/src/api/caca/mod.rs +++ b/src/api/caca/mod.rs @@ -1,11 +1,13 @@ -#![cfg(all(any(target_os = "linux", target_os = "freebsd"), feature="headless"))] +#![cfg(any(target_os = "linux", target_os = "freebsd"))] use libc; -use api::osmesa::OsMesaContext; +use api::osmesa::{OsMesaContext, OsMesaCreationError}; +use Api; use BuilderAttribs; use CreationError; use Event; +use GlContext; use PixelFormat; use CursorState; use MouseCursor; @@ -81,7 +83,12 @@ impl<'a> Iterator for WaitEventsIterator<'a> { impl Window { pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> { - let opengl = try!(OsMesaContext::new(builder)); + let opengl = match OsMesaContext::new(builder) { + Err(OsMesaCreationError::NotSupported) => return Err(CreationError::NotSupported), + Err(OsMesaCreationError::CreationError(e)) => return Err(e), + Ok(c) => c + }; + let opengl_dimensions = opengl.get_dimensions(); let libcaca = match ffi::LibCaca::open(&Path::new("libcaca.so.0")) { @@ -169,34 +176,6 @@ impl Window { } } - pub unsafe fn make_current(&self) { - self.opengl.make_current() - } - - pub fn is_current(&self) -> bool { - self.opengl.is_current() - } - - pub fn get_proc_address(&self, addr: &str) -> *const () { - self.opengl.get_proc_address(addr) as *const _ - } - - pub fn swap_buffers(&self) { - unsafe { - let canvas = (self.libcaca.caca_get_canvas)(self.display); - let width = (self.libcaca.caca_get_canvas_width)(canvas); - let height = (self.libcaca.caca_get_canvas_height)(canvas); - - let buffer = self.opengl.get_framebuffer().chunks(self.opengl.get_dimensions().0 as usize) - .flat_map(|i| i.iter().cloned()).rev().collect::<Vec<u32>>(); - - (self.libcaca.caca_dither_bitmap)(canvas, 0, 0, width as libc::c_int, - height as libc::c_int, self.dither, - buffer.as_ptr() as *const _); - (self.libcaca.caca_refresh_display)(self.display); - }; - } - pub fn platform_display(&self) -> *mut libc::c_void { unimplemented!() } @@ -205,10 +184,6 @@ impl Window { unimplemented!() } - pub fn get_api(&self) -> ::Api { - self.opengl.get_api() - } - pub fn get_pixel_format(&self) -> PixelFormat { unimplemented!(); } @@ -232,6 +207,44 @@ impl Window { } } +impl GlContext for Window { + unsafe fn make_current(&self) { + self.opengl.make_current() + } + + fn is_current(&self) -> bool { + self.opengl.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.opengl.get_proc_address(addr) + } + + fn swap_buffers(&self) { + unsafe { + let canvas = (self.libcaca.caca_get_canvas)(self.display); + let width = (self.libcaca.caca_get_canvas_width)(canvas); + let height = (self.libcaca.caca_get_canvas_height)(canvas); + + let buffer = self.opengl.get_framebuffer().chunks(self.opengl.get_dimensions().0 as usize) + .flat_map(|i| i.iter().cloned()).rev().collect::<Vec<u32>>(); + + (self.libcaca.caca_dither_bitmap)(canvas, 0, 0, width as libc::c_int, + height as libc::c_int, self.dither, + buffer.as_ptr() as *const _); + (self.libcaca.caca_refresh_display)(self.display); + }; + } + + fn get_api(&self) -> Api { + self.opengl.get_api() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.opengl.get_pixel_format() + } +} + impl Drop for Window { fn drop(&mut self) { unsafe { diff --git a/src/api/cocoa/headless.rs b/src/api/cocoa/headless.rs index 298027f..75cca8d 100644 --- a/src/api/cocoa/headless.rs +++ b/src/api/cocoa/headless.rs @@ -1,6 +1,7 @@ use CreationError; use CreationError::OsError; use BuilderAttribs; +use GlContext; use libc; use std::ptr; @@ -9,6 +10,7 @@ use core_foundation::string::CFString; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; use cocoa::base::{id, nil}; use cocoa::appkit::*; +use PixelFormat; mod gl { include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); @@ -56,8 +58,10 @@ impl HeadlessContext { Ok(headless) } +} - pub unsafe fn make_current(&self) { +impl GlContext for HeadlessContext { + unsafe fn make_current(&self) { self.context.makeCurrentContext(); gl::GenFramebuffersEXT(1, &mut framebuffer); @@ -76,11 +80,11 @@ impl HeadlessContext { } } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unimplemented!() } - pub fn get_proc_address(&self, _addr: &str) -> *const () { + fn get_proc_address(&self, _addr: &str) -> *const libc::c_void { let symbol_name: CFString = _addr.parse().unwrap(); let framework_name: CFString = "com.apple.opengl".parse().unwrap(); let framework = unsafe { @@ -89,12 +93,19 @@ impl HeadlessContext { let symbol = unsafe { CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) }; - symbol as *const () + symbol as *const libc::c_void + } + + fn swap_buffers(&self) { } - pub fn get_api(&self) -> ::Api { + fn get_api(&self) -> ::Api { ::Api::OpenGl } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); + } } unsafe impl Send for HeadlessContext {} diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs index 3254ee6..55ae617 100644 --- a/src/api/cocoa/mod.rs +++ b/src/api/cocoa/mod.rs @@ -1,6 +1,5 @@ #![cfg(target_os = "macos")] -#[cfg(feature = "headless")] pub use self::headless::HeadlessContext; use {CreationError, Event, MouseCursor, CursorState}; @@ -9,6 +8,8 @@ use libc; use Api; use BuilderAttribs; +use GlContext; +use GlProfile; use GlRequest; use PixelFormat; use native_monitor::NativeMonitorId; @@ -27,7 +28,7 @@ use core_foundation::base::TCFType; use core_foundation::string::CFString; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; -use core_graphics::display::{CGMainDisplayID, CGDisplayPixelsHigh}; +use core_graphics::display::{CGAssociateMouseAndMouseCursorPosition, CGMainDisplayID, CGDisplayPixelsHigh}; use std::ffi::CStr; use std::collections::VecDeque; @@ -46,8 +47,6 @@ pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; mod monitor; mod event; - -#[cfg(feature = "headless")] mod headless; static mut shift_pressed: bool = false; @@ -156,6 +155,7 @@ pub struct Window { view: IdRef, window: IdRef, context: IdRef, + pixel_format: PixelFormat, delegate: WindowDelegate, } @@ -326,9 +326,8 @@ impl Window { Some(app) => app, None => { return Err(OsError(format!("Couldn't create NSApplication"))); }, }; - let window = match Window::create_window(builder.dimensions.unwrap_or((800, 600)), - &*builder.title, - &builder.monitor) + + let window = match Window::create_window(&builder) { Some(window) => window, None => { return Err(OsError(format!("Couldn't create NSWindow"))); }, @@ -338,9 +337,11 @@ impl Window { None => { return Err(OsError(format!("Couldn't create NSView"))); }, }; - let context = match Window::create_context(*view, &builder) { - Some(context) => context, - None => { return Err(OsError(format!("Couldn't create OpenGL context"))); }, + // TODO: perhaps we should return error from create_context so we can + // determine the cause of failure and possibly recover? + let (context, pf) = match Window::create_context(*view, &builder) { + Ok((Some(context), Some(pf))) => (context, pf), + _ => { return Err(OsError(format!("Couldn't create OpenGL context"))); }, }; unsafe { @@ -365,6 +366,7 @@ impl Window { view: view, window: window, context: context, + pixel_format: pf, delegate: WindowDelegate::new(ds), }; @@ -384,9 +386,9 @@ impl Window { } } - fn create_window(dimensions: (u32, u32), title: &str, monitor: &Option<MonitorID>) -> Option<IdRef> { + fn create_window(builder: &BuilderAttribs) -> Option<IdRef> { unsafe { - let screen = match *monitor { + let screen = match builder.monitor { Some(ref monitor_id) => { let native_id = match monitor_id.get_native_identifier() { NativeMonitorId::Numeric(num) => num, @@ -418,7 +420,7 @@ impl Window { let frame = match screen { Some(screen) => NSScreen::frame(screen), None => { - let (width, height) = dimensions; + let (width, height) = builder.dimensions.unwrap_or((800, 600)); NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64)) } }; @@ -439,7 +441,7 @@ impl Window { NO, )); window.non_nil().map(|window| { - let title = IdRef::new(NSString::alloc(nil).init_str(title)); + let title = IdRef::new(NSString::alloc(nil).init_str(&builder.title)); window.setTitle_(*title); window.setAcceptsMouseMovedEvents_(YES); if screen.is_some() { @@ -464,53 +466,106 @@ impl Window { } } - fn create_context(view: id, builder: &BuilderAttribs) -> Option<IdRef> { - let profile = match builder.gl_version { - GlRequest::Latest => NSOpenGLProfileVersion4_1Core as u32, - GlRequest::Specific(Api::OpenGl, (1 ... 2, _)) => NSOpenGLProfileVersionLegacy as u32, - GlRequest::Specific(Api::OpenGl, (3, 0)) => NSOpenGLProfileVersionLegacy as u32, - GlRequest::Specific(Api::OpenGl, (3, 1 ... 2)) => NSOpenGLProfileVersion3_2Core as u32, - GlRequest::Specific(Api::OpenGl, _) => NSOpenGLProfileVersion4_1Core as u32, - GlRequest::Specific(_, _) => panic!("Only the OpenGL API is supported"), // FIXME: return Result - GlRequest::GlThenGles { opengl_version: (1 ... 2, _), .. } => NSOpenGLProfileVersionLegacy as u32, - GlRequest::GlThenGles { opengl_version: (3, 0), .. } => NSOpenGLProfileVersionLegacy as u32, - GlRequest::GlThenGles { opengl_version: (3, 1 ... 2), .. } => NSOpenGLProfileVersion3_2Core as u32, - GlRequest::GlThenGles { .. } => NSOpenGLProfileVersion4_1Core as u32, + fn create_context(view: id, builder: &BuilderAttribs) -> Result<(Option<IdRef>, Option<PixelFormat>), CreationError> { + let profile = match (builder.gl_version, builder.gl_version.to_gl_version(), builder.gl_profile) { + (GlRequest::Latest, _, Some(GlProfile::Compatibility)) => NSOpenGLProfileVersionLegacy as u32, + (GlRequest::Latest, _, _) => NSOpenGLProfileVersion4_1Core as u32, + (_, Some((1 ... 2, _)), Some(GlProfile::Core)) | + (_, Some((3 ... 4, _)), Some(GlProfile::Compatibility)) => + return Err(CreationError::NotSupported), + (_, Some((1 ... 2, _)), _) => NSOpenGLProfileVersionLegacy as u32, + (_, Some((3, 0 ... 2)), _) => NSOpenGLProfileVersion3_2Core as u32, + (_, Some((3 ... 4, _)), _) => NSOpenGLProfileVersion4_1Core as u32, + _ => return Err(CreationError::NotSupported), }; - unsafe { - let mut attributes = vec![ - NSOpenGLPFADoubleBuffer as u32, - NSOpenGLPFAClosestPolicy as u32, - NSOpenGLPFAColorSize as u32, 24, - NSOpenGLPFAAlphaSize as u32, 8, - NSOpenGLPFADepthSize as u32, 24, - NSOpenGLPFAStencilSize as u32, 8, - NSOpenGLPFAOpenGLProfile as u32, profile, - ]; - - if let Some(samples) = builder.multisampling { - attributes = attributes + &[ - NSOpenGLPFAMultisample as u32, - NSOpenGLPFASampleBuffers as u32, 1, - NSOpenGLPFASamples as u32, samples as u32, - ]; - } - attributes.push(0); + // NOTE: OS X no longer has the concept of setting individual + // color component's bit size. Instead we can only specify the + // full color size and hope for the best. Another hiccup is that + // `NSOpenGLPFAColorSize` also includes `NSOpenGLPFAAlphaSize`, + // so we have to account for that as well. + let alpha_depth = builder.alpha_bits.unwrap_or(8); + let color_depth = builder.color_bits.unwrap_or(24) + alpha_depth; + + let mut attributes = vec![ + NSOpenGLPFADoubleBuffer as u32, + NSOpenGLPFAClosestPolicy as u32, + NSOpenGLPFAColorSize as u32, color_depth as u32, + NSOpenGLPFAAlphaSize as u32, alpha_depth as u32, + NSOpenGLPFADepthSize as u32, builder.depth_bits.unwrap_or(24) as u32, + NSOpenGLPFAStencilSize as u32, builder.stencil_bits.unwrap_or(8) as u32, + NSOpenGLPFAOpenGLProfile as u32, profile, + ]; + + // A color depth higher than 64 implies we're using either 16-bit + // floats or 32-bit floats and OS X requires a flag to be set + // accordingly. + if color_depth >= 64 { + attributes.push(NSOpenGLPFAColorFloat as u32); + } + builder.multisampling.map(|samples| { + attributes.push(NSOpenGLPFAMultisample as u32); + attributes.push(NSOpenGLPFASampleBuffers as u32); attributes.push(1); + attributes.push(NSOpenGLPFASamples as u32); attributes.push(samples as u32); + }); + + // attribute list must be null terminated. + attributes.push(0); + + Ok(unsafe { let pixelformat = IdRef::new(NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes)); - pixelformat.non_nil().map(|pixelformat| { + + if let Some(pixelformat) = pixelformat.non_nil() { + + // TODO: Add context sharing let context = IdRef::new(NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(*pixelformat, nil)); - context.non_nil().map(|context| { - context.setView_(view); + + if let Some(cxt) = context.non_nil() { + let pf = { + let get_attr = |attrib: NSOpenGLPixelFormatAttribute| -> i32 { + let mut value = 0; + + NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_( + *pixelformat, + &mut value, + attrib, + NSOpenGLContext::currentVirtualScreen(*cxt)); + + value + }; + + PixelFormat { + hardware_accelerated: get_attr(NSOpenGLPFAAccelerated) != 0, + color_bits: (get_attr(NSOpenGLPFAColorSize) - get_attr(NSOpenGLPFAAlphaSize)) as u8, + alpha_bits: get_attr(NSOpenGLPFAAlphaSize) as u8, + depth_bits: get_attr(NSOpenGLPFADepthSize) as u8, + stencil_bits: get_attr(NSOpenGLPFAStencilSize) as u8, + stereoscopy: get_attr(NSOpenGLPFAStereo) != 0, + double_buffer: get_attr(NSOpenGLPFADoubleBuffer) != 0, + multisampling: if get_attr(NSOpenGLPFAMultisample) > 0 { + Some(get_attr(NSOpenGLPFASamples) as u16) + } else { + None + }, + srgb: true, + } + }; + + cxt.setView_(view); if builder.vsync { let value = 1; - context.setValues_forParameter_(&value, NSOpenGLContextParameter::NSOpenGLCPSwapInterval); + cxt.setValues_forParameter_(&value, NSOpenGLContextParameter::NSOpenGLCPSwapInterval); } - context - }) - }).unwrap_or(None) - } + + (Some(cxt), Some(pf)) + } else { + (None, None) + } + } else { + (None, None) + } + }) } pub fn is_closed(&self) -> bool { @@ -608,39 +663,6 @@ impl Window { return None; } - pub unsafe fn make_current(&self) { - let _: () = msg_send![*self.context, update]; - self.context.makeCurrentContext(); - } - - pub fn is_current(&self) -> bool { - unsafe { - let current = NSOpenGLContext::currentContext(nil); - if current != nil { - let is_equal: BOOL = msg_send![current, isEqual:*self.context]; - is_equal != NO - } else { - false - } - } - } - - pub fn get_proc_address(&self, _addr: &str) -> *const () { - let symbol_name: CFString = FromStr::from_str(_addr).unwrap(); - let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap(); - let framework = unsafe { - CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) - }; - let symbol = unsafe { - CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) - }; - symbol as *const () - } - - pub fn swap_buffers(&self) { - unsafe { self.context.flushBuffer(); } - } - pub fn platform_display(&self) -> *mut libc::c_void { unimplemented!() } @@ -649,14 +671,6 @@ impl Window { unimplemented!() } - pub fn get_api(&self) -> ::Api { - ::Api::OpenGl - } - - pub fn get_pixel_format(&self) -> PixelFormat { - unimplemented!(); - } - pub fn set_window_resize_callback(&mut self, callback: Option<fn(u32, u32)>) { self.delegate.state.resize_handler = callback; } @@ -701,9 +715,12 @@ impl Window { pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> { let cls = Class::get("NSCursor").unwrap(); + + // TODO: Check for errors. match state { CursorState::Normal => { let _: () = unsafe { msg_send![cls, unhide] }; + let _: i32 = unsafe { CGAssociateMouseAndMouseCursorPosition(true) }; Ok(()) }, CursorState::Hide => { @@ -711,7 +728,8 @@ impl Window { Ok(()) }, CursorState::Grab => { - Err("Mouse grabbing is unimplemented".to_string()) + let _: i32 = unsafe { CGAssociateMouseAndMouseCursorPosition(false) }; + Ok(()) } } } @@ -727,6 +745,49 @@ impl Window { } } +impl GlContext for Window { + unsafe fn make_current(&self) { + let _: () = msg_send![*self.context, update]; + self.context.makeCurrentContext(); + } + + fn is_current(&self) -> bool { + unsafe { + let current = NSOpenGLContext::currentContext(nil); + if current != nil { + let is_equal: BOOL = msg_send![current, isEqual:*self.context]; + is_equal != NO + } else { + false + } + } + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + let symbol_name: CFString = FromStr::from_str(addr).unwrap(); + let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap(); + let framework = unsafe { + CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) + }; + let symbol = unsafe { + CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) + }; + symbol as *const _ + } + + fn swap_buffers(&self) { + unsafe { self.context.flushBuffer(); } + } + + fn get_api(&self) -> ::Api { + ::Api::OpenGl + } + + fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format.clone() + } +} + struct IdRef(id); impl IdRef { diff --git a/src/api/egl/ffi.rs b/src/api/egl/ffi.rs index 8e2528d..5fa6384 100644 --- a/src/api/egl/ffi.rs +++ b/src/api/egl/ffi.rs @@ -2,8 +2,6 @@ use libc; #[cfg(target_os = "windows")] extern crate winapi; -#[cfg(target_os = "android")] -use api::android::ffi; pub mod egl { pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t; @@ -32,4 +30,4 @@ pub type EGLNativeWindowType = winapi::HWND; #[cfg(target_os = "linux")] pub type EGLNativeWindowType = *const libc::c_void; #[cfg(target_os = "android")] -pub type EGLNativeWindowType = *const ffi::ANativeWindow; +pub type EGLNativeWindowType = *const libc::c_void; diff --git a/src/api/egl/mod.rs b/src/api/egl/mod.rs index fdea2cd..b2fe2c6 100644 --- a/src/api/egl/mod.rs +++ b/src/api/egl/mod.rs @@ -1,8 +1,10 @@ -#![cfg(target_os = "linux")] +#![cfg(any(target_os = "linux", target_os = "android"))] use BuilderAttribs; use CreationError; +use GlContext; use GlRequest; +use PixelFormat; use Api; use libc; @@ -16,6 +18,8 @@ pub struct Context { display: ffi::egl::types::EGLDisplay, context: ffi::egl::types::EGLContext, surface: ffi::egl::types::EGLSurface, + api: Api, + pixel_format: PixelFormat, } impl Context { @@ -35,7 +39,7 @@ impl Context { display }; - let (_major, _minor) = unsafe { + let egl_version = unsafe { let mut major: ffi::egl::types::EGLint = mem::uninitialized(); let mut minor: ffi::egl::types::EGLint = mem::uninitialized(); @@ -46,62 +50,61 @@ impl Context { (major, minor) }; - let use_gles2 = match builder.gl_version { - GlRequest::Specific(Api::OpenGlEs, (2, _)) => true, - GlRequest::Specific(Api::OpenGlEs, _) => false, - GlRequest::Specific(_, _) => return Err(CreationError::NotSupported), - GlRequest::GlThenGles { opengles_version: (2, _), .. } => true, - _ => false, - }; - - let mut attribute_list = vec!(); - attribute_list.push(ffi::egl::RENDERABLE_TYPE as i32); - attribute_list.push(ffi::egl::OPENGL_ES2_BIT as i32); - - attribute_list.push(ffi::egl::CONFORMANT as i32); - attribute_list.push(ffi::egl::OPENGL_ES2_BIT as i32); - - attribute_list.push(ffi::egl::SURFACE_TYPE as i32); - attribute_list.push(ffi::egl::WINDOW_BIT as i32); - - { - let (red, green, blue) = match builder.color_bits.unwrap_or(24) { - 24 => (8, 8, 8), - 16 => (6, 5, 6), - _ => panic!("Bad color_bits"), - }; - - attribute_list.push(ffi::egl::RED_SIZE as i32); - attribute_list.push(red); - attribute_list.push(ffi::egl::GREEN_SIZE as i32); - attribute_list.push(green); - attribute_list.push(ffi::egl::BLUE_SIZE as i32); - attribute_list.push(blue); - } - - attribute_list.push(ffi::egl::DEPTH_SIZE as i32); - attribute_list.push(builder.depth_bits.unwrap_or(8) as i32); - - attribute_list.push(ffi::egl::NONE as i32); - - let config = unsafe { - let mut num_config: ffi::egl::types::EGLint = mem::uninitialized(); - let mut config: ffi::egl::types::EGLConfig = mem::uninitialized(); - if egl.ChooseConfig(display, attribute_list.as_ptr(), &mut config, 1, - &mut num_config) == 0 - { - return Err(CreationError::OsError(format!("eglChooseConfig failed"))) + // binding the right API and choosing the version + let (version, api) = unsafe { + match builder.gl_version { + GlRequest::Latest => { + if egl_version >= (1, 4) { + if egl.BindAPI(ffi::egl::OPENGL_API) != 0 { + (None, Api::OpenGl) + } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 { + (None, Api::OpenGlEs) + } else { + return Err(CreationError::NotSupported); + } + } else { + (None, Api::OpenGlEs) + } + }, + GlRequest::Specific(Api::OpenGlEs, version) => { + if egl_version >= (1, 2) { + if egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 { + return Err(CreationError::NotSupported); + } + } + (Some(version), Api::OpenGlEs) + }, + GlRequest::Specific(Api::OpenGl, version) => { + if egl_version < (1, 4) { + return Err(CreationError::NotSupported); + } + if egl.BindAPI(ffi::egl::OPENGL_API) == 0 { + return Err(CreationError::NotSupported); + } + (Some(version), Api::OpenGl) + }, + GlRequest::Specific(_, _) => return Err(CreationError::NotSupported), + GlRequest::GlThenGles { opengles_version, opengl_version } => { + if egl_version >= (1, 4) { + if egl.BindAPI(ffi::egl::OPENGL_API) != 0 { + (Some(opengl_version), Api::OpenGl) + } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 { + (Some(opengles_version), Api::OpenGlEs) + } else { + return Err(CreationError::NotSupported); + } + } else { + (Some(opengles_version), Api::OpenGlEs) + } + }, } - - if num_config <= 0 { - return Err(CreationError::OsError(format!("eglChooseConfig returned no available config"))) - } - - config }; + let configs = unsafe { try!(enumerate_configs(&egl, display, &egl_version, api, version)) }; + let (config_id, pixel_format) = try!(builder.choose_pixel_format(configs.into_iter())); + let surface = unsafe { - let surface = egl.CreateWindowSurface(display, config, native_window, ptr::null()); + let surface = egl.CreateWindowSurface(display, config_id, native_window, ptr::null()); if surface.is_null() { return Err(CreationError::OsError(format!("eglCreateWindowSurface failed"))) } @@ -109,17 +112,40 @@ impl Context { }; let context = unsafe { - let mut context_attributes = vec!(); - context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); - context_attributes.push(2); - context_attributes.push(ffi::egl::NONE as i32); - - let context = egl.CreateContext(display, config, ptr::null(), - context_attributes.as_ptr()); - if context.is_null() { - return Err(CreationError::OsError(format!("eglCreateContext failed"))) + if let Some(version) = version { + try!(create_context(&egl, display, &egl_version, api, version, config_id, + builder.gl_debug).map_err(|_| CreationError::NotSupported)) + + } else if api == Api::OpenGlEs { + if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (2, 0), + config_id, builder.gl_debug) + { + ctxt + } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (1, 0), + config_id, builder.gl_debug) + { + ctxt + } else { + return Err(CreationError::NotSupported); + } + + } else { + if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (3, 2), + config_id, builder.gl_debug) + { + ctxt + } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (3, 1), + config_id, builder.gl_debug) + { + ctxt + } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (1, 0), + config_id, builder.gl_debug) + { + ctxt + } else { + return Err(CreationError::NotSupported); + } } - context }; Ok(Context { @@ -127,10 +153,14 @@ impl Context { display: display, context: context, surface: surface, + api: api, + pixel_format: pixel_format, }) } +} - pub fn make_current(&self) { +impl GlContext for Context { + unsafe fn make_current(&self) { let ret = unsafe { self.egl.MakeCurrent(self.display, self.surface, self.surface, self.context) }; @@ -140,19 +170,19 @@ impl Context { } } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unsafe { self.egl.GetCurrentContext() == self.context } } - pub fn get_proc_address(&self, addr: &str) -> *const () { + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); unsafe { - self.egl.GetProcAddress(addr) as *const () + self.egl.GetProcAddress(addr) as *const _ } } - pub fn swap_buffers(&self) { + fn swap_buffers(&self) { let ret = unsafe { self.egl.SwapBuffers(self.display, self.surface) }; @@ -162,8 +192,12 @@ impl Context { } } - pub fn get_api(&self) -> ::Api { - ::Api::OpenGlEs + fn get_api(&self) -> Api { + self.api + } + + fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format.clone() } } @@ -183,3 +217,143 @@ impl Drop for Context { } } } + +unsafe fn enumerate_configs(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay, + egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), + api: Api, version: Option<(u8, u8)>) + -> Result<Vec<(ffi::egl::types::EGLConfig, PixelFormat)>, CreationError> +{ + let mut num_configs = mem::uninitialized(); + if egl.GetConfigs(display, ptr::null_mut(), 0, &mut num_configs) == 0 { + return Err(CreationError::OsError(format!("eglGetConfigs failed"))); + } + + let mut configs_ids = Vec::with_capacity(num_configs as usize); + if egl.GetConfigs(display, configs_ids.as_mut_ptr(), + configs_ids.capacity() as ffi::egl::types::EGLint, + &mut num_configs) == 0 + { + return Err(CreationError::OsError(format!("eglGetConfigs failed"))); + } + configs_ids.set_len(num_configs as usize); + + // analyzing each config + let mut result = Vec::with_capacity(num_configs as usize); + for config_id in configs_ids { + macro_rules! attrib { + ($egl:expr, $display:expr, $config:expr, $attr:expr) => ( + { + let mut value = mem::uninitialized(); + let res = $egl.GetConfigAttrib($display, $config, + $attr as ffi::egl::types::EGLint, &mut value); + if res == 0 { + return Err(CreationError::OsError(format!("eglGetConfigAttrib failed"))); + } + value + } + ) + }; + + let renderable = attrib!(egl, display, config_id, ffi::egl::RENDERABLE_TYPE) as u32; + let conformant = attrib!(egl, display, config_id, ffi::egl::CONFORMANT) as u32; + + if api == Api::OpenGlEs { + if let Some(version) = version { + if version.0 == 3 && (renderable & ffi::egl::OPENGL_ES3_BIT == 0 || + conformant & ffi::egl::OPENGL_ES3_BIT == 0) + { + continue; + } + + if version.0 == 2 && (renderable & ffi::egl::OPENGL_ES2_BIT == 0 || + conformant & ffi::egl::OPENGL_ES2_BIT == 0) + { + continue; + } + + if version.0 == 1 && (renderable & ffi::egl::OPENGL_ES_BIT == 0 || + conformant & ffi::egl::OPENGL_ES_BIT == 0) + { + continue; + } + } + + } else if api == Api::OpenGl { + if renderable & ffi::egl::OPENGL_BIT == 0 || + conformant & ffi::egl::OPENGL_BIT == 0 + { + continue; + } + } + + if attrib!(egl, display, config_id, ffi::egl::SURFACE_TYPE) & ffi::egl::WINDOW_BIT as i32 == 0 { + continue; + } + + if attrib!(egl, display, config_id, ffi::egl::TRANSPARENT_TYPE) != ffi::egl::NONE as i32 { + continue; + } + + if attrib!(egl, display, config_id, ffi::egl::COLOR_BUFFER_TYPE) != ffi::egl::RGB_BUFFER as i32 { + continue; + } + + result.push((config_id, PixelFormat { + hardware_accelerated: attrib!(egl, display, config_id, ffi::egl::CONFIG_CAVEAT) + != ffi::egl::SLOW_CONFIG as i32, + color_bits: attrib!(egl, display, config_id, ffi::egl::RED_SIZE) as u8 + + attrib!(egl, display, config_id, ffi::egl::BLUE_SIZE) as u8 + + attrib!(egl, display, config_id, ffi::egl::GREEN_SIZE) as u8, + alpha_bits: attrib!(egl, display, config_id, ffi::egl::ALPHA_SIZE) as u8, + depth_bits: attrib!(egl, display, config_id, ffi::egl::DEPTH_SIZE) as u8, + stencil_bits: attrib!(egl, display, config_id, ffi::egl::STENCIL_SIZE) as u8, + stereoscopy: false, + double_buffer: true, + multisampling: match attrib!(egl, display, config_id, ffi::egl::SAMPLES) { + 0 | 1 => None, + a => Some(a as u16), + }, + srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that + })); + } + + Ok(result) +} + +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, ()> +{ + let mut context_attributes = vec![]; + + if egl_version >= &(1, 5) { + context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); + context_attributes.push(version.0 as i32); + context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); + context_attributes.push(version.1 as i32); + + if gl_debug { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); + context_attributes.push(ffi::egl::TRUE as i32); + } + + } else { + if api == Api::OpenGlEs { + context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); + context_attributes.push(version.0 as i32); + } + } + + context_attributes.push(ffi::egl::NONE as i32); + + let context = egl.CreateContext(display, config_id, ptr::null(), + context_attributes.as_ptr()); + + if context.is_null() { + return Err(()); + } + + Ok(context) +} diff --git a/src/api/emscripten/ffi.rs b/src/api/emscripten/ffi.rs new file mode 100644 index 0000000..34b6c17 --- /dev/null +++ b/src/api/emscripten/ffi.rs @@ -0,0 +1,82 @@ +#![allow(dead_code)] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] + +use libc; + +pub type EM_BOOL = libc::c_int; +pub type EM_UTF8 = libc::c_char; +pub type EMSCRIPTEN_WEBGL_CONTEXT_HANDLE = libc::c_int; +pub type EMSCRIPTEN_RESULT = libc::c_int; + +pub type em_webgl_context_callback = extern fn(libc::c_int, *const libc::c_void, *mut libc::c_void) + -> EM_BOOL; + +#[repr(C)] +pub struct EmscriptenWebGLContextAttributes { + pub alpha: EM_BOOL, + pub depth: EM_BOOL, + pub stencil: EM_BOOL, + pub antialias: EM_BOOL, + pub premultipliedAlpha: EM_BOOL, + pub preserveDrawingBuffer: EM_BOOL, + pub preferLowPowerToHighPerformance: EM_BOOL, + pub failIfMajorPerformanceCaveat: EM_BOOL, + pub majorVersion: libc::c_int, + pub minorVersion: libc::c_int, + pub enableExtensionsByDefault: EM_BOOL, +} + +// values for EMSCRIPTEN_RESULT +pub const EMSCRIPTEN_RESULT_SUCCESS: libc::c_int = 0; +pub const EMSCRIPTEN_RESULT_DEFERRED: libc::c_int = 1; +pub const EMSCRIPTEN_RESULT_NOT_SUPPORTED: libc::c_int = -1; +pub const EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED: libc::c_int = -2; +pub const EMSCRIPTEN_RESULT_INVALID_TARGET: libc::c_int = -3; +pub const EMSCRIPTEN_RESULT_UNKNOWN_TARGET: libc::c_int = -4; +pub const EMSCRIPTEN_RESULT_INVALID_PARAM: libc::c_int = -5; +pub const EMSCRIPTEN_RESULT_FAILED: libc::c_int = -6; +pub const EMSCRIPTEN_RESULT_NO_DATA: libc::c_int = -7; + +extern { + pub fn emscripten_webgl_init_context_attributes(attributes: *mut EmscriptenWebGLContextAttributes); + pub fn emscripten_webgl_create_context(target: *const libc::c_char, + attributes: *const EmscriptenWebGLContextAttributes) -> EMSCRIPTEN_WEBGL_CONTEXT_HANDLE; + + pub fn emscripten_webgl_make_context_current(context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE) + -> EMSCRIPTEN_RESULT; + + pub fn emscripten_webgl_get_current_context() -> EMSCRIPTEN_WEBGL_CONTEXT_HANDLE; + + pub fn emscripten_webgl_destroy_context(context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE) + -> EMSCRIPTEN_RESULT; + + pub fn emscripten_webgl_enable_extension(context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE, + extension: *const libc::c_char) -> EM_BOOL; + + pub fn emscripten_set_webglcontextlost_callback(target: *const libc::c_char, + userData: *mut libc::c_void, useCapture: EM_BOOL, callback: em_webgl_context_callback) + -> EMSCRIPTEN_RESULT; + pub fn emscripten_set_webglcontextrestored_callback(target: *const libc::c_char, + userData: *mut libc::c_void, useCapture: EM_BOOL, callback: em_webgl_context_callback) + -> EMSCRIPTEN_RESULT; + + pub fn emscripten_is_webgl_context_lost(target: *const libc::c_char) -> EM_BOOL; + + // note: this function is not documented but is used by the ports of glfw, SDL and EGL + pub fn emscripten_GetProcAddress(name: *const libc::c_char) -> *const libc::c_void; + + + pub fn emscripten_request_fullscreen(target: *const libc::c_char, + deferUntilInEventHandler: EM_BOOL) -> EMSCRIPTEN_RESULT; + + pub fn emscripten_exit_fullscreen() -> EMSCRIPTEN_RESULT; + + pub fn emscripten_set_element_css_size(target: *const libc::c_char, width: libc::c_double, + height: libc::c_double) -> EMSCRIPTEN_RESULT; + + pub fn emscripten_get_element_css_size(target: *const libc::c_char, width: *mut libc::c_double, + height: *mut libc::c_double) -> EMSCRIPTEN_RESULT; + + pub fn emscripten_sleep(delay: libc::c_uint); +} diff --git a/src/api/emscripten/mod.rs b/src/api/emscripten/mod.rs new file mode 100644 index 0000000..48b31a9 --- /dev/null +++ b/src/api/emscripten/mod.rs @@ -0,0 +1,251 @@ +#![cfg(target_os = "emscripten")] + +use std::ffi::CString; +use libc; +use {Event, BuilderAttribs, CreationError, MouseCursor}; +use Api; +use PixelFormat; +use GlContext; + +use std::collections::VecDeque; + +mod ffi; + +pub struct Window { + context: ffi::EMSCRIPTEN_WEBGL_CONTEXT_HANDLE, +} + +pub struct PollEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for PollEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + None + } +} + +pub struct WaitEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for WaitEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + None + } +} + +#[derive(Clone)] +pub struct WindowProxy; + +impl WindowProxy { + pub fn wakeup_event_loop(&self) { + unimplemented!() + } +} + +pub struct MonitorID; + +pub fn get_available_monitors() -> VecDeque<MonitorID> { + let mut list = VecDeque::new(); + list.push_back(MonitorID); + list +} + +pub fn get_primary_monitor() -> MonitorID { + MonitorID +} + +impl MonitorID { + pub fn get_name(&self) -> Option<String> { + Some("Canvas".to_string()) + } + + pub fn get_dimensions(&self) -> (u32, u32) { + unimplemented!() + } +} + +impl Window { + pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> { + // getting the default values of attributes + let mut attributes = unsafe { + use std::mem; + let mut attributes: ffi::EmscriptenWebGLContextAttributes = mem::uninitialized(); + ffi::emscripten_webgl_init_context_attributes(&mut attributes); + attributes + }; + + // setting the attributes + // FIXME: + /*match builder.gl_version { + Some((major, minor)) => { + attributes.majorVersion = major as libc::c_int; + attributes.minorVersion = minor as libc::c_int; + }, + None => () + };*/ + + // creating the context + let context = unsafe { + use std::{mem, ptr}; + let context = ffi::emscripten_webgl_create_context(ptr::null(), &attributes); + if context <= 0 { + return Err(CreationError::OsError(format!("Error while calling emscripten_webgl_create_context: {}", + error_to_str(mem::transmute(context))))); + } + context + }; + + // TODO: emscripten_set_webglcontextrestored_callback + + Ok(Window { + context: context + }) + } + + pub fn is_closed(&self) -> bool { + use std::ptr; + unsafe { ffi::emscripten_is_webgl_context_lost(ptr::null()) != 0 } + } + + pub fn set_title(&self, _title: &str) { + } + + pub fn get_position(&self) -> Option<(i32, i32)> { + Some((0, 0)) + } + + pub fn set_position(&self, _: i32, _: i32) { + } + + pub fn get_inner_size(&self) -> Option<(u32, u32)> { + unsafe { + use std::{mem, ptr}; + let mut width = mem::uninitialized(); + let mut height = mem::uninitialized(); + + if ffi::emscripten_get_element_css_size(ptr::null(), &mut width, &mut height) + != ffi::EMSCRIPTEN_RESULT_SUCCESS + { + None + } else { + Some((width as u32, height as u32)) + } + } + } + + pub fn get_outer_size(&self) -> Option<(u32, u32)> { + self.get_inner_size() + } + + pub fn set_inner_size(&self, width: u32, height: u32) { + unsafe { + use std::ptr; + ffi::emscripten_set_element_css_size(ptr::null(), width as libc::c_double, height + as libc::c_double); + } + } + + pub fn poll_events(&self) -> PollEventsIterator { + PollEventsIterator { + window: self, + } + } + + pub fn wait_events(&self) -> WaitEventsIterator { + WaitEventsIterator { + window: self, + } + } + + pub fn create_window_proxy(&self) -> WindowProxy { + WindowProxy + } + + pub fn show(&self) {} + pub fn hide(&self) {} + + pub fn platform_display(&self) -> *mut libc::c_void { + unimplemented!() + } + + pub fn platform_window(&self) -> *mut libc::c_void { + unimplemented!() + } + + pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { + } + + pub fn set_cursor(&self, _cursor: MouseCursor) { + unimplemented!() + } + + pub fn hidpi_factor(&self) -> f32 { + 1.0 + } +} + +impl GlContext for Window { + unsafe fn make_current(&self) { + // TOOD: check if == EMSCRIPTEN_RESULT + ffi::emscripten_webgl_make_context_current(self.context); + } + + fn is_current(&self) -> bool { + true // FIXME: + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + let addr = CString::new(addr.as_bytes()).unwrap(); + let addr = addr.as_ptr(); + + unsafe { + ffi::emscripten_GetProcAddress(addr) as *const _ + } + } + + fn swap_buffers(&self) { + unsafe { + ffi::emscripten_sleep(1); // FIXME: + } + } + + fn get_api(&self) -> Api { + Api::WebGl + } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); + } +} + +impl Drop for Window { + fn drop(&mut self) { + unsafe { + ffi::emscripten_exit_fullscreen(); + ffi::emscripten_webgl_destroy_context(self.context); + } + } +} + +fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str { + match code { + ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED + => "Internal error in the library (success detected as failure)", + + ffi::EMSCRIPTEN_RESULT_NOT_SUPPORTED => "Not supported", + ffi::EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED => "Failed not deferred", + ffi::EMSCRIPTEN_RESULT_INVALID_TARGET => "Invalid target", + ffi::EMSCRIPTEN_RESULT_UNKNOWN_TARGET => "Unknown target", + ffi::EMSCRIPTEN_RESULT_INVALID_PARAM => "Invalid parameter", + ffi::EMSCRIPTEN_RESULT_FAILED => "Failed", + ffi::EMSCRIPTEN_RESULT_NO_DATA => "No data", + + _ => "Undocumented error" + } +} diff --git a/src/api/glx/mod.rs b/src/api/glx/mod.rs index fb86dfd..cb61dfa 100644 --- a/src/api/glx/mod.rs +++ b/src/api/glx/mod.rs @@ -2,8 +2,11 @@ use BuilderAttribs; use CreationError; +use GlContext; +use GlProfile; use GlRequest; use Api; +use PixelFormat; use libc; use std::ffi::CString; @@ -50,6 +53,17 @@ impl Context { }, } + if let Some(profile) = builder.gl_profile { + let flag = match profile { + GlProfile::Compatibility => + ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + GlProfile::Core => + ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, + }; + attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as libc::c_int); + attributes.push(flag as libc::c_int); + } + if builder.gl_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); @@ -140,35 +154,41 @@ impl Context { context: context, }) } +} - pub fn make_current(&self) { - let res = unsafe { ffi::glx::MakeCurrent(self.display as *mut _, self.window, self.context) }; +impl GlContext for Context { + unsafe fn make_current(&self) { + let res = ffi::glx::MakeCurrent(self.display as *mut _, self.window, self.context); if res == 0 { panic!("glx::MakeCurrent failed"); } } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unsafe { ffi::glx::GetCurrentContext() == self.context } } - pub fn get_proc_address(&self, addr: &str) -> *const () { + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); unsafe { - ffi::glx::GetProcAddress(addr as *const _) as *const () + ffi::glx::GetProcAddress(addr as *const _) as *const _ } } - pub fn swap_buffers(&self) { + fn swap_buffers(&self) { unsafe { ffi::glx::SwapBuffers(self.display as *mut _, self.window) } } - pub fn get_api(&self) -> ::Api { + fn get_api(&self) -> ::Api { ::Api::OpenGl } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); + } } unsafe impl Send for Context {} @@ -176,8 +196,6 @@ unsafe impl Sync for Context {} impl Drop for Context { fn drop(&mut self) { - use std::ptr; - unsafe { // we don't call MakeCurrent(0, 0) because we are not sure that the context // is still the current one diff --git a/src/api/mod.rs b/src/api/mod.rs index b8cf9de..b1a7249 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -3,6 +3,7 @@ pub mod caca; pub mod cocoa; pub mod dlopen; pub mod egl; +pub mod emscripten; pub mod glx; pub mod osmesa; pub mod win32; diff --git a/src/api/osmesa/mod.rs b/src/api/osmesa/mod.rs index ea90583..22df72a 100644 --- a/src/api/osmesa/mod.rs +++ b/src/api/osmesa/mod.rs @@ -1,10 +1,12 @@ -#![cfg(all(any(target_os = "linux", target_os = "freebsd"), feature="headless"))] +#![cfg(any(target_os = "linux", target_os = "freebsd"))] extern crate osmesa_sys; +use Api; use BuilderAttribs; use CreationError; -use CreationError::OsError; +use GlContext; +use PixelFormat; use libc; use std::{mem, ptr}; use std::ffi::CString; @@ -16,8 +18,23 @@ pub struct OsMesaContext { height: u32, } +pub enum OsMesaCreationError { + CreationError(CreationError), + NotSupported, +} + +impl From<CreationError> for OsMesaCreationError { + fn from(e: CreationError) -> OsMesaCreationError { + OsMesaCreationError::CreationError(e) + } +} + impl OsMesaContext { - pub fn new(builder: BuilderAttribs) -> Result<OsMesaContext, CreationError> { + pub fn new(builder: BuilderAttribs) -> Result<OsMesaContext, OsMesaCreationError> { + if let Err(_) = osmesa_sys::OsMesa::try_loading() { + return Err(OsMesaCreationError::NotSupported); + } + let dimensions = builder.dimensions.unwrap(); Ok(OsMesaContext { @@ -28,7 +45,7 @@ impl OsMesaContext { context: unsafe { let ctxt = osmesa_sys::OSMesaCreateContext(0x1908, ptr::null_mut()); if ctxt.is_null() { - return Err(OsError("OSMesaCreateContext failed".to_string())); + return Err(CreationError::OsError("OSMesaCreateContext failed".to_string()).into()); } ctxt } @@ -43,7 +60,13 @@ impl OsMesaContext { (self.width, self.height) } - pub unsafe fn make_current(&self) { + // TODO: can we remove this without causing havoc? + pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { + } +} + +impl GlContext for OsMesaContext { + unsafe fn make_current(&self) { let ret = osmesa_sys::OSMesaMakeCurrent(self.context, self.buffer.as_ptr() as *mut libc::c_void, 0x1401, self.width as libc::c_int, self.height as libc::c_int); @@ -53,23 +76,26 @@ impl OsMesaContext { } } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unsafe { osmesa_sys::OSMesaGetCurrentContext() == self.context } } - pub fn get_proc_address(&self, addr: &str) -> *const () { + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { unsafe { let c_str = CString::new(addr.as_bytes().to_vec()).unwrap(); mem::transmute(osmesa_sys::OSMesaGetProcAddress(mem::transmute(c_str.as_ptr()))) } } - /// See the docs in the crate root file. - pub fn get_api(&self) -> ::Api { - ::Api::OpenGl + fn swap_buffers(&self) { } - pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { + fn get_api(&self) -> Api { + Api::OpenGl + } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); } } diff --git a/src/api/win32/init.rs b/src/api/win32/init.rs index 5cdd6b8..e5b0b3a 100644 --- a/src/api/win32/init.rs +++ b/src/api/win32/init.rs @@ -17,6 +17,7 @@ use BuilderAttribs; use CreationError; use CreationError::OsError; use CursorState; +use GlProfile; use GlRequest; use PixelFormat; @@ -205,7 +206,12 @@ unsafe fn init(title: Vec<u16>, builder: BuilderAttribs<'static>, // calling SetPixelFormat let pixel_format = { let formats = if extra_functions.GetPixelFormatAttribivARB.is_loaded() { - enumerate_arb_pixel_formats(&extra_functions, &real_window) + let f = enumerate_arb_pixel_formats(&extra_functions, &real_window); + if f.is_empty() { + enumerate_native_pixel_formats(&real_window) + } else { + f + } } else { enumerate_native_pixel_formats(&real_window) }; @@ -369,6 +375,23 @@ unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'st }, } + if let Some(profile) = builder.gl_profile { + if is_extension_supported(extra_functions, hdc, + "WGL_ARB_create_context_profile") + { + let flag = match profile { + GlProfile::Compatibility => + gl::wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + GlProfile::Core => + gl::wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB, + }; + attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as libc::c_int); + attributes.push(flag as libc::c_int); + } else { + return Err(CreationError::NotSupported); + } + } + 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); @@ -433,9 +456,7 @@ unsafe fn enumerate_native_pixel_formats(hdc: &WindowWrapper) -> Vec<(PixelForma result.push((PixelFormat { hardware_accelerated: (output.dwFlags & winapi::PFD_GENERIC_FORMAT) == 0, - red_bits: output.cRedBits, - green_bits: output.cGreenBits, - blue_bits: output.cBlueBits, + color_bits: output.cRedBits + output.cGreenBits + output.cBlueBits, alpha_bits: output.cAlphaBits, depth_bits: output.cDepthBits, stencil_bits: output.cStencilBits, @@ -484,9 +505,9 @@ unsafe fn enumerate_arb_pixel_formats(extra: &gl::wgl_extra::Wgl, hdc: &WindowWr result.push((PixelFormat { hardware_accelerated: true, - red_bits: get_info(index, gl::wgl_extra::RED_BITS_ARB) as u8, - green_bits: get_info(index, gl::wgl_extra::GREEN_BITS_ARB) as u8, - blue_bits: get_info(index, gl::wgl_extra::BLUE_BITS_ARB) as u8, + color_bits: get_info(index, gl::wgl_extra::RED_BITS_ARB) as u8 + + get_info(index, gl::wgl_extra::GREEN_BITS_ARB) as u8 + + get_info(index, gl::wgl_extra::BLUE_BITS_ARB) as u8, alpha_bits: get_info(index, gl::wgl_extra::ALPHA_BITS_ARB) as u8, depth_bits: get_info(index, gl::wgl_extra::DEPTH_BITS_ARB) as u8, stencil_bits: get_info(index, gl::wgl_extra::STENCIL_BITS_ARB) as u8, diff --git a/src/api/win32/mod.rs b/src/api/win32/mod.rs index dd3e685..d0caea7 100644 --- a/src/api/win32/mod.rs +++ b/src/api/win32/mod.rs @@ -14,7 +14,9 @@ use std::sync::mpsc::Receiver; use libc; use {CreationError, Event, MouseCursor}; use CursorState; +use GlContext; +use Api; use PixelFormat; use BuilderAttribs; @@ -195,7 +197,7 @@ impl Window { unsafe { user32::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, x as libc::c_int, - y as libc::c_int, winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION); + y as libc::c_int, winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION | winapi::SWP_NOMOVE); user32::UpdateWindow(self.window.0); } } @@ -218,37 +220,6 @@ impl Window { } } - /// See the docs in the crate root file. - pub unsafe fn make_current(&self) { - // TODO: check return value - gl::wgl::MakeCurrent(self.window.1 as *const libc::c_void, - self.context.0 as *const libc::c_void); - } - - /// See the docs in the crate root file. - pub fn is_current(&self) -> bool { - unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const libc::c_void } - } - - /// See the docs in the crate root file. - pub fn get_proc_address(&self, addr: &str) -> *const () { - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - - unsafe { - let p = gl::wgl::GetProcAddress(addr) as *const (); - if !p.is_null() { return p; } - kernel32::GetProcAddress(self.gl_library, addr) as *const () - } - } - - /// See the docs in the crate root file. - pub fn swap_buffers(&self) { - unsafe { - gdi32::SwapBuffers(self.window.1); - } - } - pub fn platform_display(&self) -> *mut libc::c_void { unimplemented!() } @@ -257,15 +228,6 @@ impl Window { self.window.0 as *mut libc::c_void } - /// See the docs in the crate root file. - pub fn get_api(&self) -> ::Api { - ::Api::OpenGl - } - - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() - } - pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { } @@ -362,6 +324,43 @@ impl Window { } } +impl GlContext for Window { + unsafe fn make_current(&self) { + // TODO: check return value + gl::wgl::MakeCurrent(self.window.1 as *const libc::c_void, + self.context.0 as *const libc::c_void); + } + + fn is_current(&self) -> bool { + unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const libc::c_void } + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + let addr = CString::new(addr.as_bytes()).unwrap(); + let addr = addr.as_ptr(); + + unsafe { + let p = gl::wgl::GetProcAddress(addr) as *const _; + if !p.is_null() { return p; } + kernel32::GetProcAddress(self.gl_library, addr) as *const _ + } + } + + fn swap_buffers(&self) { + unsafe { + gdi32::SwapBuffers(self.window.1); + } + } + + fn get_api(&self) -> Api { + Api::OpenGl + } + + fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format.clone() + } +} + pub struct PollEventsIterator<'a> { window: &'a Window, } diff --git a/src/api/x11/mod.rs b/src/api/x11/mod.rs index 252b22e..7d7c048 100644 --- a/src/api/x11/mod.rs +++ b/src/api/x11/mod.rs @@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex, Once, ONCE_INIT}; use Api; use CursorState; +use GlContext; use GlRequest; use PixelFormat; @@ -411,9 +412,9 @@ impl Window { PixelFormat { hardware_accelerated: true, - red_bits: get_attrib(ffi::glx::RED_SIZE as libc::c_int) as u8, - green_bits: get_attrib(ffi::glx::GREEN_SIZE as libc::c_int) as u8, - blue_bits: get_attrib(ffi::glx::BLUE_SIZE as libc::c_int) as u8, + color_bits: get_attrib(ffi::glx::RED_SIZE as libc::c_int) as u8 + + get_attrib(ffi::glx::GREEN_SIZE as libc::c_int) as u8 + + get_attrib(ffi::glx::BLUE_SIZE as libc::c_int) as u8, alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as libc::c_int) as u8, depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as libc::c_int) as u8, stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as libc::c_int) as u8, @@ -674,38 +675,6 @@ impl Window { } } - pub unsafe fn make_current(&self) { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.make_current(), - Context::Egl(ref ctxt) => ctxt.make_current(), - Context::None => {} - } - } - - pub fn is_current(&self) -> bool { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.is_current(), - Context::Egl(ref ctxt) => ctxt.is_current(), - Context::None => panic!() - } - } - - pub fn get_proc_address(&self, addr: &str) -> *const () { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.get_proc_address(addr), - Context::Egl(ref ctxt) => ctxt.get_proc_address(addr), - Context::None => ptr::null() - } - } - - pub fn swap_buffers(&self) { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.swap_buffers(), - Context::Egl(ref ctxt) => ctxt.swap_buffers(), - Context::None => {} - } - } - pub fn platform_display(&self) -> *mut libc::c_void { self.x.display as *mut libc::c_void } @@ -714,18 +683,6 @@ impl Window { unimplemented!() } - /// See the docs in the crate root file. - pub fn get_api(&self) -> ::Api { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.get_api(), - Context::Egl(ref ctxt) => ctxt.get_api(), - Context::None => panic!() - } - } - - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() - } pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { } @@ -828,3 +785,49 @@ impl Window { Ok(()) } } + +impl GlContext for Window { + unsafe fn make_current(&self) { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.make_current(), + Context::Egl(ref ctxt) => ctxt.make_current(), + Context::None => {} + } + } + + fn is_current(&self) -> bool { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.is_current(), + Context::Egl(ref ctxt) => ctxt.is_current(), + Context::None => panic!() + } + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.get_proc_address(addr), + Context::Egl(ref ctxt) => ctxt.get_proc_address(addr), + Context::None => ptr::null() + } + } + + fn swap_buffers(&self) { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.swap_buffers(), + Context::Egl(ref ctxt) => ctxt.swap_buffers(), + Context::None => {} + } + } + + fn get_api(&self) -> Api { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.get_api(), + Context::Egl(ref ctxt) => ctxt.get_api(), + Context::None => panic!() + } + } + + fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format.clone() + } +} |