diff options
-rw-r--r-- | src/api/glx/mod.rs | 187 | ||||
-rw-r--r-- | src/api/mod.rs | 1 | ||||
-rw-r--r-- | src/api/x11/mod.rs | 170 |
3 files changed, 239 insertions, 119 deletions
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..c71c239 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,6 +1,7 @@ pub mod android; pub mod cocoa; 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..0bb5c88 100644 --- a/src/api/x11/mod.rs +++ b/src/api/x11/mod.rs @@ -15,11 +15,13 @@ use CursorState; use GlRequest; use PixelFormat; +use api::glx::Context as GlxContext; + 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 +44,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 +62,11 @@ struct XWindow { im: ffi::XIM, } +pub enum Context { + Glx(GlxContext), + None, +} + unsafe impl Send for XWindow {} unsafe impl Sync for XWindow {} @@ -70,7 +78,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 +290,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,109 +536,22 @@ 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) + 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 egl = ::egl::ffi::egl::Egl; + Context::Egl(try!(EglContext::new(egl, builder, Some(display as *const _), window))) + },*/ + GlRequest::Specific(_, _) => { + return Err(CreationError::NotSupported); + }, }; - // 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); - } - - } 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()) }; - } - // creating the window object let window = Window { x: Arc::new(XWindow { @@ -640,7 +561,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 +664,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 +705,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 { |