diff options
-rw-r--r-- | .travis.yml | 5 | ||||
-rw-r--r-- | Cargo.toml | 11 | ||||
-rw-r--r-- | README.md | 33 | ||||
-rw-r--r-- | appveyor.yml | 3 | ||||
-rw-r--r-- | build.rs | 3 | ||||
-rw-r--r-- | examples/support/mod.rs | 13 | ||||
-rw-r--r-- | examples/window.rs | 2 | ||||
-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 | ||||
-rw-r--r-- | src/headless.rs | 28 | ||||
-rw-r--r-- | src/lib.rs | 64 | ||||
-rw-r--r-- | src/platform/android/mod.rs | 31 | ||||
-rw-r--r-- | src/platform/emscripten/mod.rs | 44 | ||||
-rw-r--r-- | src/platform/linux/mod.rs | 56 | ||||
-rw-r--r-- | src/platform/windows/mod.rs | 22 | ||||
-rw-r--r-- | src/window.rs | 35 |
29 files changed, 1309 insertions, 596 deletions
diff --git a/.travis.yml b/.travis.yml index bc52bee..1551440 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: rust rust: - nightly - - 1.0.0-beta.2 + - 1.0.0-beta.3 addons: apt: @@ -17,8 +17,7 @@ env: script: - cargo build --verbose - - cargo test --no-run --verbose - - cargo test --verbose --features "headless" --no-default-features + - cargo test --verbose os: - linux @@ -1,6 +1,6 @@ [package] name = "glutin" -version = "0.0.27" +version = "0.0.31" authors = ["tomaka <pierre.krieger1708@gmail.com>"] description = "Cross-plaform OpenGL context provider." keywords = ["windowing", "opengl"] @@ -13,7 +13,6 @@ build = "build.rs" [features] default = ["window"] window = [] -headless = [] [dependencies] gl_common = "*" @@ -50,9 +49,13 @@ user32-sys = "0.1" kernel32-sys = "0.1" [target.i686-unknown-linux-gnu.dependencies] -osmesa-sys = "*" +osmesa-sys = "0.0.5" x11 = "*" [target.x86_64-unknown-linux-gnu.dependencies] -osmesa-sys = "*" +osmesa-sys = "0.0.5" +x11 = "*" + +[target.arm-unknown-linux-gnueabihf.dependencies] +osmesa-sys = "0.0.5" x11 = "*" @@ -1,6 +1,8 @@ # glutin - OpenGL, UTilities and INput [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/tomaka/glutin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![](http://meritbadge.herokuapp.com/glutin)](https://crates.io/crates/glutin) + Alternative to GLFW in pure Rust. [![Build Status](https://travis-ci.org/tomaka/glutin.png?branch=master)](https://travis-ci.org/tomaka/glutin) @@ -11,13 +13,6 @@ Alternative to GLFW in pure Rust. glutin = "*" ``` -Note that the crates.io version won't compile on OS/X and Android because the required dependencies haven't been uploaded yet. Instead you can use the git version which works everywhere: - -```toml -[dependencies.glutin] -git = "https://github.com/tomaka/glutin" -``` - ## [Documentation](http://tomaka.github.io/glutin/) ## Try it! @@ -64,32 +59,14 @@ fn main() { } ``` +Note that glutin aims at being a low-level brick in your rendering infrastructure. You are encouraged to write another layer of abstraction between glutin and your application. + ## Platform-specific notes ### Android - To compile the examples for android, initialize the submodules, go to `deps/apk-builder/apk-builder` and run `cargo build`, then go back to `glutin` and call `ANDROID_HOME=/path/to/sdk NDK_HOME=/path/to/ndk NDK_STANDALONE=/path/to/standalone cargo test --no-run --target=arm-linux-androideabi` - - Events and vsync are not implemented - - Headless rendering doesn't work - -### Emscripten - - - Work will start when Emscripten gets updated to LLVM 3.5 (which should happen soon) - -### OS/X - - - Some events are not implemented - - Implementation is still work-in-progress - - Vsync not implemented - -### Win32 - - - You must call `glFlush` before `swap_buffers`, or else on Windows 8 nothing will be visible on the window - - Changing the cursor (set_cursor) is not implemented ### X11 - - Some input events are not implemented - - Pixel formats not implemented - - Vsync not implemented - - Not all mouse cursors are implemented (ContextMenu, ...) + - The plan is that glutin tries to dynamically link-to and use wayland if possible. If it doesn't work, it will try xlib instead. If it doesn't work, it will try libcaca. This is work-in-progress. diff --git a/appveyor.yml b/appveyor.yml index b7a558c..cc2c0d8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,5 +9,4 @@ install: build: false test_script: - - cargo test --verbose --no-default-features --features "window" - - cargo test --verbose --no-default-features --features "headless" + - cargo test --verbose @@ -52,6 +52,7 @@ fn main() { khronos_api::GLX_XML, vec![ "GLX_ARB_create_context".to_string(), + "GLX_ARB_create_context_profile".to_string(), "GLX_ARB_framebuffer_sRGB".to_string(), "GLX_EXT_framebuffer_sRGB".to_string(), "GLX_EXT_swap_control".to_string(), @@ -70,7 +71,7 @@ fn main() { if target.contains("android") { let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); - gl_generator::generate_bindings(gl_generator::StaticGenerator, + gl_generator::generate_bindings(gl_generator::StaticStructGenerator, gl_generator::registry::Ns::Egl, gl_generator::Fallbacks::All, khronos_api::EGL_XML, vec![], diff --git a/examples/support/mod.rs b/examples/support/mod.rs index 329bd4b..ac41239 100644 --- a/examples/support/mod.rs +++ b/examples/support/mod.rs @@ -53,6 +53,8 @@ impl Context { #[cfg(target_os = "android")] pub fn draw_frame(&self, color: (f32, f32, f32, f32)) { + use std::mem; + unsafe { self.gl.ClearColor(color.0, color.1, color.2, color.3); self.gl.Clear(gl::COLOR_BUFFER_BIT); @@ -60,13 +62,10 @@ impl Context { self.gl.EnableClientState(gl::VERTEX_ARRAY); self.gl.EnableClientState(gl::COLOR_ARRAY); - unsafe { - use std::mem; - self.gl.VertexPointer(2, gl::FLOAT, (mem::size_of::<f32>() * 5) as i32, - mem::transmute(VERTEX_DATA.as_slice().as_ptr())); - self.gl.ColorPointer(3, gl::FLOAT, (mem::size_of::<f32>() * 5) as i32, - mem::transmute(VERTEX_DATA.as_slice().as_ptr().offset(2))); - } + self.gl.VertexPointer(2, gl::FLOAT, (mem::size_of::<f32>() * 5) as i32, + mem::transmute(VERTEX_DATA.as_ptr())); + self.gl.ColorPointer(3, gl::FLOAT, (mem::size_of::<f32>() * 5) as i32, + mem::transmute(VERTEX_DATA.as_ptr().offset(2))); self.gl.DrawArrays(gl::TRIANGLES, 0, 3); self.gl.DisableClientState(gl::VERTEX_ARRAY); diff --git a/examples/window.rs b/examples/window.rs index 1d3b615..4077399 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -24,6 +24,8 @@ fn main() { window.set_window_resize_callback(Some(resize_callback as fn(u32, u32))); unsafe { window.make_current() }; + println!("Pixel format of the window: {:?}", window.get_pixel_format()); + let context = support::load(&window); while !window.is_closed() { 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() + } +} diff --git a/src/headless.rs b/src/headless.rs index dc42108..566cbee 100644 --- a/src/headless.rs +++ b/src/headless.rs @@ -2,6 +2,8 @@ use Api; use BuilderAttribs; use CreationError; use GlRequest; +use GlContext; +use PixelFormat; use gl_common; use libc; @@ -101,3 +103,29 @@ impl gl_common::GlFunctionsSource for HeadlessContext { self.get_proc_address(addr) } } + +impl GlContext for HeadlessContext { + unsafe fn make_current(&self) { + self.make_current() + } + + fn is_current(&self) -> bool { + self.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.get_proc_address(addr) + } + + fn swap_buffers(&self) { + self.swap_buffers() + } + + fn get_api(&self) -> Api { + self.get_api() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.get_pixel_format() + } +} @@ -53,7 +53,6 @@ extern crate glutin_core_graphics as core_graphics; extern crate x11; pub use events::*; -#[cfg(feature = "headless")] pub use headless::{HeadlessRendererBuilder, HeadlessContext}; #[cfg(feature = "window")] pub use window::{WindowBuilder, Window, WindowProxy, PollEventsIterator, WaitEventsIterator}; @@ -65,11 +64,38 @@ pub use native_monitor::NativeMonitorId; mod api; mod platform; mod events; -#[cfg(feature = "headless")] mod headless; #[cfg(feature = "window")] mod window; +/// Trait that describes objects that have access to an OpenGL context. +pub trait GlContext { + /// Sets the context as the current context. + unsafe fn make_current(&self); + + /// Returns true if this context is the current one in this thread. + fn is_current(&self) -> bool; + + /// Returns the address of an OpenGL function. + fn get_proc_address(&self, addr: &str) -> *const libc::c_void; + + /// Swaps the buffers in case of double or triple buffering. + /// + /// You should call this function every time you have finished rendering, or the image + /// may not be displayed on the screen. + /// + /// **Warning**: if you enabled vsync, this function will block until the next time the screen + /// is refreshed. However drivers can choose to override your vsync settings, which means that + /// you can't know in advance whether `swap_buffers` will block or not. + fn swap_buffers(&self); + + /// Returns the OpenGL API being used. + fn get_api(&self) -> Api; + + /// Returns the pixel format of the main framebuffer of the context. + fn get_pixel_format(&self) -> PixelFormat; +} + /// Error that can happen while creating a window or a headless renderer. #[derive(Clone, Debug, PartialEq, Eq)] pub enum CreationError { @@ -109,6 +135,15 @@ pub enum Api { WebGl, } +/// Describes the requested OpenGL context profiles. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GlProfile { + /// Include all the immediate more functions and definitions. + Compatibility, + /// Include all the future-compatible functions and definitions. + Core, +} + /// Describes the OpenGL API and version that are being requested when a context is created. #[derive(Debug, Copy, Clone)] pub enum GlRequest { @@ -133,6 +168,22 @@ pub enum GlRequest { }, } +impl GlRequest { + /// Extract the desktop GL version, if any. + pub fn to_gl_version(&self) -> Option<(u8, u8)> { + match self { + &GlRequest::Specific(Api::OpenGl, version) => Some(version), + &GlRequest::GlThenGles { opengl_version: version, .. } => Some(version), + _ => None, + } + } +} + +/// The minimum core profile GL context. Useful for getting the minimum +/// required GL version while still running on OSX, which often forbids +/// the compatibility profile features. +pub static GL_CORE: GlRequest = GlRequest::Specific(Api::OpenGl, (3, 2)); + #[derive(Debug, Copy, Clone)] pub enum MouseCursor { /// The platform-dependent default cursor. @@ -211,9 +262,7 @@ pub enum CursorState { #[derive(Debug, Clone)] pub struct PixelFormat { pub hardware_accelerated: bool, - pub red_bits: u8, - pub green_bits: u8, - pub blue_bits: u8, + pub color_bits: u8, pub alpha_bits: u8, pub depth_bits: u8, pub stencil_bits: u8, @@ -235,6 +284,7 @@ pub struct BuilderAttribs<'a> { title: String, monitor: Option<platform::MonitorID>, gl_version: GlRequest, + gl_profile: Option<GlProfile>, gl_debug: bool, vsync: bool, visible: bool, @@ -257,6 +307,7 @@ impl BuilderAttribs<'static> { title: "glutin window".to_string(), monitor: None, gl_version: GlRequest::Latest, + gl_profile: None, gl_debug: cfg!(debug_assertions), vsync: false, visible: true, @@ -283,6 +334,7 @@ impl<'a> BuilderAttribs<'a> { title: self.title, monitor: self.monitor, gl_version: self.gl_version, + gl_profile: self.gl_profile, gl_debug: self.gl_debug, vsync: self.vsync, visible: self.visible, @@ -306,7 +358,7 @@ impl<'a> BuilderAttribs<'a> { // TODO: do this more properly for (id, format) in iter { - if format.red_bits + format.green_bits + format.blue_bits < self.color_bits.unwrap_or(0) { + if format.color_bits < self.color_bits.unwrap_or(0) { continue; } diff --git a/src/platform/android/mod.rs b/src/platform/android/mod.rs index c90d8ce..a1b9416 100644 --- a/src/platform/android/mod.rs +++ b/src/platform/android/mod.rs @@ -1,3 +1,34 @@ #![cfg(target_os = "android")] pub use api::android::*; + +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!() + } + + /// 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 + } +} + +unsafe impl Send for HeadlessContext {} +unsafe impl Sync for HeadlessContext {} diff --git a/src/platform/emscripten/mod.rs b/src/platform/emscripten/mod.rs new file mode 100644 index 0000000..56a7e9f --- /dev/null +++ b/src/platform/emscripten/mod.rs @@ -0,0 +1,44 @@ +#![cfg(target_os = "emscripten")] + +use GlContext; + +pub use api::emscripten::{Window, WindowProxy, MonitorID, get_available_monitors}; +pub use api::emscripten::{get_primary_monitor, WaitEventsIterator, PollEventsIterator}; + +pub struct HeadlessContext(Window); + +impl HeadlessContext { + /// See the docs in the crate root file. + pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> { + Window::new(builder).map(|w| HeadlessContext(w)) + } +} + +impl GlContext for HeadlessContext { + unsafe fn make_current(&self) { + self.0.make_current() + } + + fn is_current(&self) -> bool { + self.0.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.0.get_proc_address(addr) + } + + fn swap_buffers(&self) { + self.0.swap_buffers() + } + + fn get_api(&self) -> Api { + self.0.get_api() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.0.get_pixel_format() + } +} + +unsafe impl Send for HeadlessContext {} +unsafe impl Sync for HeadlessContext {} diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 63a0118..1e12a80 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,7 +1,13 @@ #![cfg(target_os = "linux")] -#[cfg(feature = "headless")] -pub use api::osmesa::OsMesaContext as HeadlessContext; +use Api; +use BuilderAttribs; +use CreationError; +use GlContext; +use PixelFormat; +use libc; + +use api::osmesa::{self, OsMesaContext}; #[cfg(feature = "window")] pub use api::x11::{Window, WindowProxy, MonitorID, get_available_monitors, get_primary_monitor}; @@ -12,3 +18,49 @@ pub use api::x11::{WaitEventsIterator, PollEventsIterator}; pub type Window = (); // TODO: hack to make things work #[cfg(not(feature = "window"))] pub type MonitorID = (); // TODO: hack to make things work + +pub struct HeadlessContext(OsMesaContext); + +impl HeadlessContext { + pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> { + match OsMesaContext::new(builder) { + Ok(c) => return Ok(HeadlessContext(c)), + Err(osmesa::OsMesaCreationError::NotSupported) => (), + Err(osmesa::OsMesaCreationError::CreationError(e)) => return Err(e), + }; + + Err(CreationError::NotSupported) + } +} + +impl GlContext for HeadlessContext { + #[inline] + unsafe fn make_current(&self) { + self.0.make_current() + } + + #[inline] + fn is_current(&self) -> bool { + self.0.is_current() + } + + #[inline] + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.0.get_proc_address(addr) + } + + #[inline] + fn swap_buffers(&self) { + self.0.swap_buffers() + } + + #[inline] + fn get_api(&self) -> Api { + self.0.get_api() + } + + #[inline] + fn get_pixel_format(&self) -> PixelFormat { + self.0.get_pixel_format() + } +} diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 78cbefc..260ab94 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -2,9 +2,13 @@ pub use api::win32::*; +use libc; + use Api; use BuilderAttribs; use CreationError; +use PixelFormat; +use GlContext; /// pub struct HeadlessContext(Window); @@ -14,20 +18,30 @@ impl HeadlessContext { builder.visible = false; Window::new(builder).map(|w| HeadlessContext(w)) } +} - pub unsafe fn make_current(&self) { +impl GlContext for HeadlessContext { + unsafe fn make_current(&self) { self.0.make_current() } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { self.0.is_current() } - pub fn get_proc_address(&self, addr: &str) -> *const () { + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { self.0.get_proc_address(addr) } - pub fn get_api(&self) -> Api { + fn swap_buffers(&self) { + self.0.swap_buffers() + } + + fn get_api(&self) -> Api { self.0.get_api() } + + fn get_pixel_format(&self) -> PixelFormat { + self.0.get_pixel_format() + } } diff --git a/src/window.rs b/src/window.rs index 0dd9905..c21b82d 100644 --- a/src/window.rs +++ b/src/window.rs @@ -6,6 +6,8 @@ use BuilderAttribs; use CreationError; use CursorState; use Event; +use GlContext; +use GlProfile; use GlRequest; use MouseCursor; use PixelFormat; @@ -66,6 +68,12 @@ impl<'a> WindowBuilder<'a> { self } + /// Sets the desired OpenGL context profile. + pub fn with_gl_profile(mut self, profile: GlProfile) -> WindowBuilder<'a> { + self.attribs.gl_profile = Some(profile); + self + } + /// Sets the *debug* flag for the OpenGL context. /// /// The default value for this flag is `cfg!(debug_assertions)`, which means that it's enabled @@ -428,6 +436,32 @@ impl gl_common::GlFunctionsSource for Window { } } +impl GlContext for Window { + unsafe fn make_current(&self) { + self.make_current() + } + + fn is_current(&self) -> bool { + self.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.get_proc_address(addr) + } + + fn swap_buffers(&self) { + self.swap_buffers() + } + + fn get_api(&self) -> Api { + self.get_api() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.get_pixel_format() + } +} + /// Represents a thread safe subset of operations that can be called /// on a window. This structure can be safely cloned and sent between /// threads. @@ -437,7 +471,6 @@ pub struct WindowProxy { } impl WindowProxy { - /// Triggers a blocked event loop to wake up. This is /// typically called when another thread wants to wake /// up the blocked rendering thread to cause a refresh. |