aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.rs8
-rw-r--r--src/api/dlopen.rs14
-rw-r--r--src/api/egl/ffi.rs8
-rw-r--r--src/api/egl/mod.rs2
-rw-r--r--src/api/glx/mod.rs187
-rw-r--r--src/api/mod.rs2
-rw-r--r--src/api/x11/mod.rs181
7 files changed, 275 insertions, 127 deletions
diff --git a/build.rs b/build.rs
index ca95574..4f96710 100644
--- a/build.rs
+++ b/build.rs
@@ -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 {