diff options
-rw-r--r-- | build.rs | 8 | ||||
-rw-r--r-- | src/api/dlopen.rs | 14 | ||||
-rw-r--r-- | src/api/egl/ffi.rs | 8 | ||||
-rw-r--r-- | src/api/egl/mod.rs | 2 | ||||
-rw-r--r-- | src/api/glx/mod.rs | 187 | ||||
-rw-r--r-- | src/api/mod.rs | 2 | ||||
-rw-r--r-- | src/api/x11/mod.rs | 181 |
7 files changed, 275 insertions, 127 deletions
@@ -58,6 +58,14 @@ fn main() { "GLX_SGI_swap_control".to_string() ], "1.4", "core", &mut file).unwrap(); + + let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); + gl_generator::generate_bindings(gl_generator::StructGenerator, + gl_generator::registry::Ns::Egl, + gl_generator::Fallbacks::All, + khronos_api::EGL_XML, + vec![], + "1.5", "core", &mut file).unwrap(); } if target.contains("android") { diff --git a/src/api/dlopen.rs b/src/api/dlopen.rs new file mode 100644 index 0000000..63f690a --- /dev/null +++ b/src/api/dlopen.rs @@ -0,0 +1,14 @@ +#![cfg(target_os = "linux")] + +use libc; + +pub const RTLD_LAZY: libc::c_int = 0x001; +pub const RTLD_NOW: libc::c_int = 0x002; + +#[link="dl"] +extern { + pub fn dlopen(filename: *const libc::c_char, flag: libc::c_int) -> *mut libc::c_void; + pub fn dlerror() -> *mut libc::c_char; + pub fn dlsym(handle: *mut libc::c_void, symbol: *const libc::c_char) -> *mut libc::c_void; + pub fn dlclose(handle: *mut libc::c_void) -> libc::c_int; +} diff --git a/src/api/egl/ffi.rs b/src/api/egl/ffi.rs index e3cf32e..8e2528d 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 = "linux")] -use api::x11::ffi; #[cfg(target_os = "android")] use api::android::ffi; @@ -32,10 +30,6 @@ pub type EGLNativePixmapType = *const libc::c_void; // FIXME: egl_native_pix #[cfg(target_os = "windows")] pub type EGLNativeWindowType = winapi::HWND; #[cfg(target_os = "linux")] -pub type EGLNativeWindowType = ffi::Window; +pub type EGLNativeWindowType = *const libc::c_void; #[cfg(target_os = "android")] pub type EGLNativeWindowType = *const ffi::ANativeWindow; - -#[cfg(not(target_os = "windows"))] -#[link(name = "EGL")] -extern {} diff --git a/src/api/egl/mod.rs b/src/api/egl/mod.rs index d9ac313..fdea2cd 100644 --- a/src/api/egl/mod.rs +++ b/src/api/egl/mod.rs @@ -1,4 +1,4 @@ -#![cfg(all(target_os = "windows", target_os = "linux"))] // always false of the moment +#![cfg(target_os = "linux")] use BuilderAttribs; use CreationError; diff --git a/src/api/glx/mod.rs b/src/api/glx/mod.rs new file mode 100644 index 0000000..fb86dfd --- /dev/null +++ b/src/api/glx/mod.rs @@ -0,0 +1,187 @@ +#![cfg(all(target_os = "linux", feature = "window"))] + +use BuilderAttribs; +use CreationError; +use GlRequest; +use Api; + +use libc; +use std::ffi::CString; +use std::{mem, ptr}; + +use api::x11::ffi; + +pub struct Context { + display: *mut ffi::Display, + window: ffi::Window, + context: ffi::GLXContext, +} + +// TODO: remove me +fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T { + use std::ffi::CString; + let c_str = CString::new(s.as_bytes().to_vec()).unwrap(); + f(c_str.as_ptr()) +} + +impl Context { + pub fn new(builder: BuilderAttribs, display: *mut ffi::Display, window: ffi::Window, + fb_config: ffi::glx::types::GLXFBConfig, mut visual_infos: ffi::glx::types::XVisualInfo) + -> Result<Context, CreationError> + { + // creating GL context + let (context, extra_functions) = unsafe { + let mut attributes = Vec::new(); + + match builder.gl_version { + GlRequest::Latest => {}, + GlRequest::Specific(Api::OpenGl, (major, minor)) => { + attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); + attributes.push(major as libc::c_int); + attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); + attributes.push(minor as libc::c_int); + }, + GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"), + GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { + attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); + attributes.push(major as libc::c_int); + attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); + attributes.push(minor 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); + } + + attributes.push(0); + + // loading the extra GLX functions + let extra_functions = ffi::glx_extra::Glx::load_with(|addr| { + with_c_str(addr, |s| { + ffi::glx::GetProcAddress(s as *const u8) as *const libc::c_void + }) + }); + + let share = if let Some(win) = builder.sharing { + match win.x.context { + ::api::x11::Context::Glx(ref c) => c.context, + _ => panic!("Cannot share contexts between different APIs") + } + } else { + ptr::null() + }; + + let mut context = if extra_functions.CreateContextAttribsARB.is_loaded() { + extra_functions.CreateContextAttribsARB(display as *mut ffi::glx_extra::types::Display, + fb_config, share, 1, attributes.as_ptr()) + } else { + ptr::null() + }; + + if context.is_null() { + context = ffi::glx::CreateContext(display as *mut _, &mut visual_infos, share, 1) + } + + if context.is_null() { + return Err(CreationError::OsError(format!("GL context creation failed"))); + } + + (context, extra_functions) + }; + + // vsync + if builder.vsync { + unsafe { ffi::glx::MakeCurrent(display as *mut _, window, context) }; + + if extra_functions.SwapIntervalEXT.is_loaded() { + // this should be the most common extension + unsafe { + extra_functions.SwapIntervalEXT(display as *mut _, window, 1); + } + + // checking that it worked + if builder.strict { + let mut swap = unsafe { mem::uninitialized() }; + unsafe { + ffi::glx::QueryDrawable(display as *mut _, window, + ffi::glx_extra::SWAP_INTERVAL_EXT as i32, + &mut swap); + } + + if swap != 1 { + return Err(CreationError::OsError(format!("Couldn't setup vsync: expected \ + interval `1` but got `{}`", swap))); + } + } + + // GLX_MESA_swap_control is not official + /*} else if extra_functions.SwapIntervalMESA.is_loaded() { + unsafe { + extra_functions.SwapIntervalMESA(1); + }*/ + + } else if extra_functions.SwapIntervalSGI.is_loaded() { + unsafe { + extra_functions.SwapIntervalSGI(1); + } + + } else if builder.strict { + return Err(CreationError::OsError(format!("Couldn't find any available vsync extension"))); + } + + unsafe { ffi::glx::MakeCurrent(display as *mut _, 0, ptr::null()) }; + } + + Ok(Context { + display: display, + window: window, + context: context, + }) + } + + pub fn make_current(&self) { + let res = unsafe { ffi::glx::MakeCurrent(self.display as *mut _, self.window, self.context) }; + if res == 0 { + panic!("glx::MakeCurrent failed"); + } + } + + pub fn is_current(&self) -> bool { + unsafe { ffi::glx::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::glx::GetProcAddress(addr as *const _) as *const () + } + } + + pub fn swap_buffers(&self) { + unsafe { + ffi::glx::SwapBuffers(self.display as *mut _, self.window) + } + } + + pub fn get_api(&self) -> ::Api { + ::Api::OpenGl + } +} + +unsafe impl Send for Context {} +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 + ffi::glx::DestroyContext(self.display as *mut _, self.context); + } + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index a99cef7..34ef01d 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,6 +1,8 @@ pub mod android; pub mod cocoa; +pub mod dlopen; pub mod egl; +pub mod glx; pub mod osmesa; pub mod win32; pub mod x11; diff --git a/src/api/x11/mod.rs b/src/api/x11/mod.rs index c4c5a76..3dd90d3 100644 --- a/src/api/x11/mod.rs +++ b/src/api/x11/mod.rs @@ -6,6 +6,7 @@ use CreationError::OsError; use libc; use std::{mem, ptr}; use std::cell::Cell; +use std::ffi::CString; use std::sync::atomic::AtomicBool; use std::collections::VecDeque; use std::sync::{Arc, Mutex, Once, ONCE_INIT}; @@ -15,11 +16,15 @@ use CursorState; use GlRequest; use PixelFormat; +use api::dlopen; +use api::glx::Context as GlxContext; +use api::egl::Context as EglContext; + pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; mod events; -mod ffi; mod monitor; +pub mod ffi; static THREAD_INIT: Once = ONCE_INIT; @@ -42,16 +47,17 @@ fn ensure_thread_init() { }); } +// TODO: remove me fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T { use std::ffi::CString; let c_str = CString::new(s.as_bytes().to_vec()).unwrap(); f(c_str.as_ptr()) } -struct XWindow { +pub struct XWindow { display: *mut ffi::Display, window: ffi::Window, - context: ffi::GLXContext, + pub context: Context, is_fullscreen: bool, screen_id: libc::c_int, xf86_desk_mode: *mut ffi::XF86VidModeModeInfo, @@ -59,6 +65,12 @@ struct XWindow { im: ffi::XIM, } +pub enum Context { + Glx(GlxContext), + Egl(EglContext), + None, +} + unsafe impl Send for XWindow {} unsafe impl Sync for XWindow {} @@ -70,7 +82,7 @@ impl Drop for XWindow { unsafe { // we don't call MakeCurrent(0, 0) because we are not sure that the context // is still the current one - ffi::glx::DestroyContext(self.display as *mut _, self.context); + self.context = Context::None; if self.is_fullscreen { ffi::XF86VidModeSwitchToMode(self.display, self.screen_id, self.xf86_desk_mode); @@ -282,7 +294,7 @@ impl<'a> Iterator for WaitEventsIterator<'a> { } pub struct Window { - x: Arc<XWindow>, + pub x: Arc<XWindow>, is_closed: AtomicBool, wm_delete_window: ffi::Atom, current_size: Cell<(libc::c_int, libc::c_int)>, @@ -528,108 +540,28 @@ impl Window { }); } - // creating GL context - let (context, extra_functions) = unsafe { - let mut attributes = Vec::new(); - - match builder.gl_version { - GlRequest::Latest => {}, - GlRequest::Specific(Api::OpenGl, (major, minor)) => { - attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); - attributes.push(major as libc::c_int); - attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); - attributes.push(minor as libc::c_int); - }, - GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"), - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); - attributes.push(major as libc::c_int); - attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); - attributes.push(minor 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); - } - - attributes.push(0); - - // loading the extra GLX functions - let extra_functions = ffi::glx_extra::Glx::load_with(|addr| { - with_c_str(addr, |s| { - use libc; - ffi::glx::GetProcAddress(s as *const u8) as *const libc::c_void - }) - }); - - let share = if let Some(win) = builder.sharing { - win.x.context - } else { - ptr::null() - }; - - let mut context = if extra_functions.CreateContextAttribsARB.is_loaded() { - extra_functions.CreateContextAttribsARB(display as *mut ffi::glx_extra::types::Display, - fb_config, share, 1, attributes.as_ptr()) - } else { - ptr::null() - }; - - if context.is_null() { - context = ffi::glx::CreateContext(display as *mut _, &mut visual_infos, share, 1) - } - - if context.is_null() { - return Err(OsError(format!("GL context creation failed"))); - } - - (context, extra_functions) - }; - - // vsync - if builder.vsync { - unsafe { ffi::glx::MakeCurrent(display as *mut _, window, context) }; - - if extra_functions.SwapIntervalEXT.is_loaded() { - // this should be the most common extension - unsafe { - extra_functions.SwapIntervalEXT(display as *mut _, window, 1); - } - - // checking that it worked - if builder.strict { - let mut swap = unsafe { mem::uninitialized() }; - unsafe { - ffi::glx::QueryDrawable(display as *mut _, window, - ffi::glx_extra::SWAP_INTERVAL_EXT as i32, - &mut swap); - } - - if swap != 1 { - return Err(OsError(format!("Couldn't setup vsync: expected \ - interval `1` but got `{}`", swap))); - } - } - - // GLX_MESA_swap_control is not official - /*} else if extra_functions.SwapIntervalMESA.is_loaded() { - unsafe { - extra_functions.SwapIntervalMESA(1); - }*/ - - } else if extra_functions.SwapIntervalSGI.is_loaded() { - unsafe { - extra_functions.SwapIntervalSGI(1); + let is_fullscreen = builder.monitor.is_some(); + // creating the context + let context = match builder.gl_version { + GlRequest::Latest | GlRequest::Specific(Api::OpenGl, _) | GlRequest::GlThenGles { .. } => { + Context::Glx(try!(GlxContext::new(builder, display, window, + fb_config, visual_infos))) + }, + GlRequest::Specific(Api::OpenGlEs, _) => { + let libegl = unsafe { dlopen::dlopen(b"libEGL.so\0".as_ptr() as *const _, dlopen::RTLD_NOW) }; + if libegl.is_null() { + return Err(CreationError::NotSupported); } - - } else if builder.strict { - return Err(OsError(format!("Couldn't find any available vsync extension"))); - } - - unsafe { ffi::glx::MakeCurrent(display as *mut _, 0, ptr::null()) }; - } + let egl = ::api::egl::ffi::egl::Egl::load_with(|sym| { + let sym = CString::new(sym).unwrap(); + unsafe { dlopen::dlsym(libegl, sym.as_ptr()) } + }); + Context::Egl(try!(EglContext::new(egl, builder, Some(display as *const _), unsafe { mem::transmute(window) }))) + }, + GlRequest::Specific(_, _) => { + return Err(CreationError::NotSupported); + }, + }; // creating the window object let window = Window { @@ -640,7 +572,7 @@ impl Window { ic: ic, context: context, screen_id: screen_id, - is_fullscreen: builder.monitor.is_some(), + is_fullscreen: is_fullscreen, xf86_desk_mode: xf86_desk_mode, }), is_closed: AtomicBool::new(false), @@ -743,28 +675,35 @@ impl Window { } pub unsafe fn make_current(&self) { - let res = ffi::glx::MakeCurrent(self.x.display as *mut _, self.x.window, self.x.context); - if res == 0 { - panic!("glx::MakeCurrent failed"); + 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 { - unsafe { ffi::glx::GetCurrentContext() == self.x.context } + 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 () { - use std::mem; - - unsafe { - with_c_str(addr, |s| { - ffi::glx::GetProcAddress(mem::transmute(s)) as *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) { - unsafe { ffi::glx::SwapBuffers(self.x.display as *mut _, self.x.window) } + 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 { @@ -777,7 +716,11 @@ impl Window { /// See the docs in the crate root file. pub fn get_api(&self) -> ::Api { - ::Api::OpenGl + 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 { |