Split GLX and X11 in "api"

This commit is contained in:
Pierre Krieger 2015-04-24 18:52:07 +02:00
parent 7eeb96909c
commit 5561e58646
3 changed files with 239 additions and 119 deletions

187
src/api/glx/mod.rs Normal file
View file

@ -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);
}
}
}

View file

@ -1,6 +1,7 @@
pub mod android; pub mod android;
pub mod cocoa; pub mod cocoa;
pub mod egl; pub mod egl;
pub mod glx;
pub mod osmesa; pub mod osmesa;
pub mod win32; pub mod win32;
pub mod x11; pub mod x11;

View file

@ -15,11 +15,13 @@ use CursorState;
use GlRequest; use GlRequest;
use PixelFormat; use PixelFormat;
use api::glx::Context as GlxContext;
pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};
mod events; mod events;
mod ffi;
mod monitor; mod monitor;
pub mod ffi;
static THREAD_INIT: Once = ONCE_INIT; 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 { fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
use std::ffi::CString; use std::ffi::CString;
let c_str = CString::new(s.as_bytes().to_vec()).unwrap(); let c_str = CString::new(s.as_bytes().to_vec()).unwrap();
f(c_str.as_ptr()) f(c_str.as_ptr())
} }
struct XWindow { pub struct XWindow {
display: *mut ffi::Display, display: *mut ffi::Display,
window: ffi::Window, window: ffi::Window,
context: ffi::GLXContext, pub context: Context,
is_fullscreen: bool, is_fullscreen: bool,
screen_id: libc::c_int, screen_id: libc::c_int,
xf86_desk_mode: *mut ffi::XF86VidModeModeInfo, xf86_desk_mode: *mut ffi::XF86VidModeModeInfo,
@ -59,6 +62,11 @@ struct XWindow {
im: ffi::XIM, im: ffi::XIM,
} }
pub enum Context {
Glx(GlxContext),
None,
}
unsafe impl Send for XWindow {} unsafe impl Send for XWindow {}
unsafe impl Sync for XWindow {} unsafe impl Sync for XWindow {}
@ -70,7 +78,7 @@ impl Drop for XWindow {
unsafe { unsafe {
// we don't call MakeCurrent(0, 0) because we are not sure that the context // we don't call MakeCurrent(0, 0) because we are not sure that the context
// is still the current one // is still the current one
ffi::glx::DestroyContext(self.display as *mut _, self.context); self.context = Context::None;
if self.is_fullscreen { if self.is_fullscreen {
ffi::XF86VidModeSwitchToMode(self.display, self.screen_id, self.xf86_desk_mode); ffi::XF86VidModeSwitchToMode(self.display, self.screen_id, self.xf86_desk_mode);
@ -282,7 +290,7 @@ impl<'a> Iterator for WaitEventsIterator<'a> {
} }
pub struct Window { pub struct Window {
x: Arc<XWindow>, pub x: Arc<XWindow>,
is_closed: AtomicBool, is_closed: AtomicBool,
wm_delete_window: ffi::Atom, wm_delete_window: ffi::Atom,
current_size: Cell<(libc::c_int, libc::c_int)>, current_size: Cell<(libc::c_int, libc::c_int)>,
@ -528,109 +536,22 @@ impl Window {
}); });
} }
// creating GL context let is_fullscreen = builder.monitor.is_some();
let (context, extra_functions) = unsafe { // creating the context
let mut attributes = Vec::new(); let context = match builder.gl_version {
GlRequest::Latest | GlRequest::Specific(Api::OpenGl, _) | GlRequest::GlThenGles { .. } => {
match builder.gl_version { Context::Glx(try!(GlxContext::new(builder, display, window,
GlRequest::Latest => {}, fb_config, visual_infos)))
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::Specific(Api::OpenGlEs, _) => {
GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { let egl = ::egl::ffi::egl::Egl;
attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); Context::Egl(try!(EglContext::new(egl, builder, Some(display as *const _), window)))
attributes.push(major as libc::c_int); },*/
attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); GlRequest::Specific(_, _) => {
attributes.push(minor as libc::c_int); return Err(CreationError::NotSupported);
}, },
}
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);
}
} 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 // creating the window object
let window = Window { let window = Window {
x: Arc::new(XWindow { x: Arc::new(XWindow {
@ -640,7 +561,7 @@ impl Window {
ic: ic, ic: ic,
context: context, context: context,
screen_id: screen_id, screen_id: screen_id,
is_fullscreen: builder.monitor.is_some(), is_fullscreen: is_fullscreen,
xf86_desk_mode: xf86_desk_mode, xf86_desk_mode: xf86_desk_mode,
}), }),
is_closed: AtomicBool::new(false), is_closed: AtomicBool::new(false),
@ -743,28 +664,35 @@ impl Window {
} }
pub unsafe fn make_current(&self) { pub unsafe fn make_current(&self) {
let res = ffi::glx::MakeCurrent(self.x.display as *mut _, self.x.window, self.x.context); match self.x.context {
if res == 0 { Context::Glx(ref ctxt) => ctxt.make_current(),
panic!("glx::MakeCurrent failed"); //Context::Egl(ref ctxt) => ctxt.make_current(),
Context::None => {}
} }
} }
pub fn is_current(&self) -> bool { 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 () { pub fn get_proc_address(&self, addr: &str) -> *const () {
use std::mem; match self.x.context {
Context::Glx(ref ctxt) => ctxt.get_proc_address(addr),
unsafe { //Context::Egl(ref ctxt) => ctxt.get_proc_address(addr),
with_c_str(addr, |s| { Context::None => ptr::null()
ffi::glx::GetProcAddress(mem::transmute(s)) as *const ()
})
} }
} }
pub fn swap_buffers(&self) { 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 { pub fn platform_display(&self) -> *mut libc::c_void {
@ -777,7 +705,11 @@ impl Window {
/// See the docs in the crate root file. /// See the docs in the crate root file.
pub fn get_api(&self) -> ::Api { 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 { pub fn get_pixel_format(&self) -> PixelFormat {