mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 05:21:31 +11:00
Remove most OpenGL stuff and make it compile on win32
This commit is contained in:
parent
bd605478d1
commit
10bb03c5f0
25
Cargo.toml
25
Cargo.toml
|
@ -1,24 +1,19 @@
|
||||||
[package]
|
[package]
|
||||||
name = "glutin"
|
name = "winit"
|
||||||
version = "0.4.8"
|
version = "0.4.8"
|
||||||
authors = ["The glutin contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||||
description = "Cross-platform OpenGL context provider."
|
description = "Cross-platform window creation library."
|
||||||
keywords = ["windowing", "opengl"]
|
keywords = ["windowing"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/tomaka/glutin"
|
repository = "https://github.com/tomaka/winit"
|
||||||
documentation = "https://tomaka.github.io/glutin/"
|
documentation = "https://tomaka.github.io/winit/"
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "0.1.10"
|
lazy_static = "0.1.10"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
shared_library = "0.1.0"
|
shared_library = "0.1.0"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
gl_generator = "0.4"
|
|
||||||
khronos_api = "1.0"
|
|
||||||
|
|
||||||
[target.arm-linux-androideabi.dependencies.android_glue]
|
[target.arm-linux-androideabi.dependencies.android_glue]
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
|
|
||||||
|
@ -77,56 +72,48 @@ kernel32-sys = "0.2"
|
||||||
dwmapi-sys = "0.1"
|
dwmapi-sys = "0.1"
|
||||||
|
|
||||||
[target.i686-unknown-linux-gnu.dependencies]
|
[target.i686-unknown-linux-gnu.dependencies]
|
||||||
osmesa-sys = "0.0.5"
|
|
||||||
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
||||||
wayland-kbd = "0.3.3"
|
wayland-kbd = "0.3.3"
|
||||||
wayland-window = "0.2.2"
|
wayland-window = "0.2.2"
|
||||||
x11-dl = "~2.3"
|
x11-dl = "~2.3"
|
||||||
|
|
||||||
[target.i586-unknown-linux-gnu.dependencies]
|
[target.i586-unknown-linux-gnu.dependencies]
|
||||||
osmesa-sys = "0.0.5"
|
|
||||||
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
||||||
wayland-kbd = "0.3.3"
|
wayland-kbd = "0.3.3"
|
||||||
wayland-window = "0.2.2"
|
wayland-window = "0.2.2"
|
||||||
x11-dl = "~2.3"
|
x11-dl = "~2.3"
|
||||||
|
|
||||||
[target.x86_64-unknown-linux-gnu.dependencies]
|
[target.x86_64-unknown-linux-gnu.dependencies]
|
||||||
osmesa-sys = "0.0.5"
|
|
||||||
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
||||||
wayland-kbd = "0.3.3"
|
wayland-kbd = "0.3.3"
|
||||||
wayland-window = "0.2.2"
|
wayland-window = "0.2.2"
|
||||||
x11-dl = "~2.3"
|
x11-dl = "~2.3"
|
||||||
|
|
||||||
[target.arm-unknown-linux-gnueabihf.dependencies]
|
[target.arm-unknown-linux-gnueabihf.dependencies]
|
||||||
osmesa-sys = "0.0.5"
|
|
||||||
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
||||||
wayland-kbd = "0.3.3"
|
wayland-kbd = "0.3.3"
|
||||||
wayland-window = "0.2.2"
|
wayland-window = "0.2.2"
|
||||||
x11-dl = "~2.3"
|
x11-dl = "~2.3"
|
||||||
|
|
||||||
[target.armv7-unknown-linux-gnueabihf.dependencies]
|
[target.armv7-unknown-linux-gnueabihf.dependencies]
|
||||||
osmesa-sys = "0.0.5"
|
|
||||||
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
||||||
wayland-kbd = "0.3.3"
|
wayland-kbd = "0.3.3"
|
||||||
wayland-window = "0.2.2"
|
wayland-window = "0.2.2"
|
||||||
x11-dl = "~2.3"
|
x11-dl = "~2.3"
|
||||||
|
|
||||||
[target.aarch64-unknown-linux-gnu.dependencies]
|
[target.aarch64-unknown-linux-gnu.dependencies]
|
||||||
osmesa-sys = "0.0.5"
|
|
||||||
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
||||||
wayland-kbd = "0.3.3"
|
wayland-kbd = "0.3.3"
|
||||||
wayland-window = "0.2.2"
|
wayland-window = "0.2.2"
|
||||||
x11-dl = "~2.3"
|
x11-dl = "~2.3"
|
||||||
|
|
||||||
[target.x86_64-unknown-dragonfly.dependencies]
|
[target.x86_64-unknown-dragonfly.dependencies]
|
||||||
osmesa-sys = "0.0.5"
|
|
||||||
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
||||||
wayland-kbd = "0.3.3"
|
wayland-kbd = "0.3.3"
|
||||||
wayland-window = "0.2.2"
|
wayland-window = "0.2.2"
|
||||||
x11-dl = "~2.3"
|
x11-dl = "~2.3"
|
||||||
|
|
||||||
[target.x86_64-unknown-freebsd.dependencies]
|
[target.x86_64-unknown-freebsd.dependencies]
|
||||||
osmesa-sys = "0.0.5"
|
|
||||||
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
wayland-client = { version = "0.5.4", features = ["egl", "dlopen"] }
|
||||||
wayland-kbd = "0.3.3"
|
wayland-kbd = "0.3.3"
|
||||||
wayland-window = "0.2.2"
|
wayland-window = "0.2.2"
|
||||||
|
|
187
build.rs
187
build.rs
|
@ -1,187 +0,0 @@
|
||||||
extern crate gl_generator;
|
|
||||||
extern crate khronos_api;
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let target = env::var("TARGET").unwrap();
|
|
||||||
let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
|
||||||
|
|
||||||
if target.contains("windows") {
|
|
||||||
let mut file = File::create(&dest.join("wgl_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::StaticGenerator,
|
|
||||||
gl_generator::registry::Ns::Wgl,
|
|
||||||
gl_generator::Fallbacks::All,
|
|
||||||
khronos_api::WGL_XML, vec![],
|
|
||||||
"1.0", "core", &mut file).unwrap();
|
|
||||||
|
|
||||||
let mut file = File::create(&dest.join("wgl_extra_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::StructGenerator,
|
|
||||||
gl_generator::registry::Ns::Wgl,
|
|
||||||
gl_generator::Fallbacks::All,
|
|
||||||
khronos_api::WGL_XML,
|
|
||||||
vec![
|
|
||||||
"WGL_ARB_create_context".to_string(),
|
|
||||||
"WGL_ARB_create_context_profile".to_string(),
|
|
||||||
"WGL_ARB_create_context_robustness".to_string(),
|
|
||||||
"WGL_ARB_context_flush_control".to_string(),
|
|
||||||
"WGL_ARB_extensions_string".to_string(),
|
|
||||||
"WGL_ARB_framebuffer_sRGB".to_string(),
|
|
||||||
"WGL_ARB_multisample".to_string(),
|
|
||||||
"WGL_ARB_pixel_format".to_string(),
|
|
||||||
"WGL_ARB_pixel_format_float".to_string(),
|
|
||||||
"WGL_EXT_create_context_es2_profile".to_string(),
|
|
||||||
"WGL_EXT_extensions_string".to_string(),
|
|
||||||
"WGL_EXT_framebuffer_sRGB".to_string(),
|
|
||||||
"WGL_EXT_swap_control".to_string(),
|
|
||||||
],
|
|
||||||
"1.0", "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![
|
|
||||||
"EGL_KHR_create_context".to_string(),
|
|
||||||
"EGL_EXT_create_context_robustness".to_string(),
|
|
||||||
"EGL_KHR_create_context_no_error".to_string(),
|
|
||||||
"EGL_KHR_platform_x11".to_string(),
|
|
||||||
"EGL_KHR_platform_android".to_string(),
|
|
||||||
"EGL_KHR_platform_wayland".to_string(),
|
|
||||||
"EGL_KHR_platform_gbm".to_string(),
|
|
||||||
"EGL_EXT_platform_base".to_string(),
|
|
||||||
"EGL_EXT_platform_x11".to_string(),
|
|
||||||
"EGL_MESA_platform_gbm".to_string(),
|
|
||||||
"EGL_EXT_platform_wayland".to_string(),
|
|
||||||
"EGL_EXT_platform_device".to_string(),
|
|
||||||
],
|
|
||||||
"1.5", "core", &mut file).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if target.contains("linux") || target.contains("dragonfly") || target.contains("freebsd") {
|
|
||||||
let mut file = File::create(&dest.join("glx_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::StructGenerator,
|
|
||||||
gl_generator::registry::Ns::Glx,
|
|
||||||
gl_generator::Fallbacks::All,
|
|
||||||
khronos_api::GLX_XML, vec![],
|
|
||||||
"1.4", "core", &mut file).unwrap();
|
|
||||||
|
|
||||||
let mut file = File::create(&dest.join("glx_extra_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::StructGenerator,
|
|
||||||
gl_generator::registry::Ns::Glx,
|
|
||||||
gl_generator::Fallbacks::All,
|
|
||||||
khronos_api::GLX_XML,
|
|
||||||
vec![
|
|
||||||
"GLX_ARB_create_context".to_string(),
|
|
||||||
"GLX_ARB_create_context_profile".to_string(),
|
|
||||||
"GLX_ARB_create_context_robustness".to_string(),
|
|
||||||
"GLX_ARB_context_flush_control".to_string(),
|
|
||||||
"GLX_ARB_fbconfig_float".to_string(),
|
|
||||||
"GLX_ARB_framebuffer_sRGB".to_string(),
|
|
||||||
"GLX_EXT_framebuffer_sRGB".to_string(),
|
|
||||||
"GLX_ARB_multisample".to_string(),
|
|
||||||
"GLX_EXT_swap_control".to_string(),
|
|
||||||
"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![
|
|
||||||
"EGL_KHR_create_context".to_string(),
|
|
||||||
"EGL_EXT_create_context_robustness".to_string(),
|
|
||||||
"EGL_KHR_create_context_no_error".to_string(),
|
|
||||||
"EGL_KHR_platform_x11".to_string(),
|
|
||||||
"EGL_KHR_platform_android".to_string(),
|
|
||||||
"EGL_KHR_platform_wayland".to_string(),
|
|
||||||
"EGL_KHR_platform_gbm".to_string(),
|
|
||||||
"EGL_EXT_platform_base".to_string(),
|
|
||||||
"EGL_EXT_platform_x11".to_string(),
|
|
||||||
"EGL_MESA_platform_gbm".to_string(),
|
|
||||||
"EGL_EXT_platform_wayland".to_string(),
|
|
||||||
"EGL_EXT_platform_device".to_string(),
|
|
||||||
],
|
|
||||||
"1.5", "core", &mut file).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if target.contains("android") {
|
|
||||||
let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::StaticStructGenerator,
|
|
||||||
gl_generator::registry::Ns::Egl,
|
|
||||||
gl_generator::Fallbacks::All,
|
|
||||||
khronos_api::EGL_XML,
|
|
||||||
vec![
|
|
||||||
"EGL_KHR_create_context".to_string(),
|
|
||||||
"EGL_EXT_create_context_robustness".to_string(),
|
|
||||||
"EGL_KHR_create_context_no_error".to_string(),
|
|
||||||
"EGL_KHR_platform_x11".to_string(),
|
|
||||||
"EGL_KHR_platform_android".to_string(),
|
|
||||||
"EGL_KHR_platform_wayland".to_string(),
|
|
||||||
"EGL_KHR_platform_gbm".to_string(),
|
|
||||||
"EGL_EXT_platform_base".to_string(),
|
|
||||||
"EGL_EXT_platform_x11".to_string(),
|
|
||||||
"EGL_MESA_platform_gbm".to_string(),
|
|
||||||
"EGL_EXT_platform_wayland".to_string(),
|
|
||||||
"EGL_EXT_platform_device".to_string(),
|
|
||||||
],
|
|
||||||
"1.5", "core", &mut file).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if target.contains("ios") {
|
|
||||||
let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::StaticStructGenerator,
|
|
||||||
gl_generator::registry::Ns::Egl,
|
|
||||||
gl_generator::Fallbacks::All,
|
|
||||||
khronos_api::EGL_XML,
|
|
||||||
vec![
|
|
||||||
"EGL_KHR_create_context".to_string(),
|
|
||||||
"EGL_EXT_create_context_robustness".to_string(),
|
|
||||||
"EGL_KHR_create_context_no_error".to_string(),
|
|
||||||
"EGL_KHR_platform_x11".to_string(),
|
|
||||||
"EGL_KHR_platform_android".to_string(),
|
|
||||||
"EGL_KHR_platform_wayland".to_string(),
|
|
||||||
"EGL_KHR_platform_gbm".to_string(),
|
|
||||||
"EGL_EXT_platform_base".to_string(),
|
|
||||||
"EGL_EXT_platform_x11".to_string(),
|
|
||||||
"EGL_MESA_platform_gbm".to_string(),
|
|
||||||
"EGL_EXT_platform_wayland".to_string(),
|
|
||||||
"EGL_EXT_platform_device".to_string(),
|
|
||||||
],
|
|
||||||
"1.5", "core", &mut file).unwrap();
|
|
||||||
|
|
||||||
let mut file = File::create(&dest.join("gles2_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::StaticStructGenerator,
|
|
||||||
gl_generator::registry::Ns::Gles2,
|
|
||||||
gl_generator::Fallbacks::None,
|
|
||||||
khronos_api::GL_XML,
|
|
||||||
vec![],
|
|
||||||
"2.0", "core", &mut file).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if target.contains("darwin") {
|
|
||||||
let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::GlobalGenerator,
|
|
||||||
gl_generator::registry::Ns::Gl,
|
|
||||||
gl_generator::Fallbacks::All,
|
|
||||||
khronos_api::GL_XML,
|
|
||||||
vec!["GL_EXT_framebuffer_object".to_string()],
|
|
||||||
"3.2", "core", &mut file).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: only build the bindings below if we run tests/examples
|
|
||||||
|
|
||||||
let mut file = File::create(&dest.join("test_gl_bindings.rs")).unwrap();
|
|
||||||
gl_generator::generate_bindings(gl_generator::StructGenerator,
|
|
||||||
gl_generator::registry::Ns::Gles2,
|
|
||||||
gl_generator::Fallbacks::All,
|
|
||||||
khronos_api::GL_XML, vec![],
|
|
||||||
"3.0", "core", &mut file).unwrap();
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::mem;
|
|
||||||
use std::ptr;
|
|
||||||
use glutin;
|
|
||||||
|
|
||||||
mod gl {
|
|
||||||
pub use self::Gles2 as Gl;
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/test_gl_bindings.rs"));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Context {
|
|
||||||
gl: gl::Gl
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load(window: &glutin::Window) -> Context {
|
|
||||||
let gl = gl::Gl::load_with(|ptr| window.get_proc_address(ptr) as *const _);
|
|
||||||
|
|
||||||
let version = unsafe {
|
|
||||||
let data = CStr::from_ptr(gl.GetString(gl::VERSION) as *const _).to_bytes().to_vec();
|
|
||||||
String::from_utf8(data).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("OpenGL version {}", version);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let vs = gl.CreateShader(gl::VERTEX_SHADER);
|
|
||||||
gl.ShaderSource(vs, 1, [VS_SRC.as_ptr() as *const _].as_ptr(), ptr::null());
|
|
||||||
gl.CompileShader(vs);
|
|
||||||
|
|
||||||
let fs = gl.CreateShader(gl::FRAGMENT_SHADER);
|
|
||||||
gl.ShaderSource(fs, 1, [FS_SRC.as_ptr() as *const _].as_ptr(), ptr::null());
|
|
||||||
gl.CompileShader(fs);
|
|
||||||
|
|
||||||
let program = gl.CreateProgram();
|
|
||||||
gl.AttachShader(program, vs);
|
|
||||||
gl.AttachShader(program, fs);
|
|
||||||
gl.LinkProgram(program);
|
|
||||||
gl.UseProgram(program);
|
|
||||||
|
|
||||||
let mut vb = mem::uninitialized();
|
|
||||||
gl.GenBuffers(1, &mut vb);
|
|
||||||
gl.BindBuffer(gl::ARRAY_BUFFER, vb);
|
|
||||||
gl.BufferData(gl::ARRAY_BUFFER,
|
|
||||||
(VERTEX_DATA.len() * mem::size_of::<f32>()) as gl::types::GLsizeiptr,
|
|
||||||
VERTEX_DATA.as_ptr() as *const _, gl::STATIC_DRAW);
|
|
||||||
|
|
||||||
if gl.BindVertexArray.is_loaded() {
|
|
||||||
let mut vao = mem::uninitialized();
|
|
||||||
gl.GenVertexArrays(1, &mut vao);
|
|
||||||
gl.BindVertexArray(vao);
|
|
||||||
}
|
|
||||||
|
|
||||||
let pos_attrib = gl.GetAttribLocation(program, b"position\0".as_ptr() as *const _);
|
|
||||||
let color_attrib = gl.GetAttribLocation(program, b"color\0".as_ptr() as *const _);
|
|
||||||
gl.VertexAttribPointer(pos_attrib as gl::types::GLuint, 2, gl::FLOAT, 0,
|
|
||||||
5 * mem::size_of::<f32>() as gl::types::GLsizei,
|
|
||||||
ptr::null());
|
|
||||||
gl.VertexAttribPointer(color_attrib as gl::types::GLuint, 3, gl::FLOAT, 0,
|
|
||||||
5 * mem::size_of::<f32>() as gl::types::GLsizei,
|
|
||||||
(2 * mem::size_of::<f32>()) as *const () as *const _);
|
|
||||||
gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
|
|
||||||
gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
|
|
||||||
}
|
|
||||||
|
|
||||||
Context { gl: gl }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
pub fn draw_frame(&self, color: (f32, f32, f32, f32)) {
|
|
||||||
unsafe {
|
|
||||||
self.gl.ClearColor(color.0, color.1, color.2, color.3);
|
|
||||||
self.gl.Clear(gl::COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static VERTEX_DATA: [f32; 15] = [
|
|
||||||
-0.5, -0.5, 1.0, 0.0, 0.0,
|
|
||||||
0.0, 0.5, 0.0, 1.0, 0.0,
|
|
||||||
0.5, -0.5, 0.0, 0.0, 1.0
|
|
||||||
];
|
|
||||||
|
|
||||||
const VS_SRC: &'static [u8] = b"
|
|
||||||
#version 100
|
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
attribute vec2 position;
|
|
||||||
attribute vec3 color;
|
|
||||||
|
|
||||||
varying vec3 v_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
|
||||||
v_color = color;
|
|
||||||
}
|
|
||||||
\0";
|
|
||||||
|
|
||||||
const FS_SRC: &'static [u8] = b"
|
|
||||||
#version 100
|
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
varying vec3 v_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_FragColor = vec4(v_color, 1.0);
|
|
||||||
}
|
|
||||||
\0";
|
|
|
@ -2,9 +2,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate android_glue;
|
extern crate android_glue;
|
||||||
|
|
||||||
extern crate glutin;
|
extern crate winit;
|
||||||
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
android_start!(main);
|
android_start!(main);
|
||||||
|
@ -14,23 +12,15 @@ fn resize_callback(width: u32, height: u32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut window = glutin::WindowBuilder::new().build().unwrap();
|
let mut window = winit::WindowBuilder::new().build().unwrap();
|
||||||
window.set_title("A fantastic window!");
|
window.set_title("A fantastic window!");
|
||||||
window.set_window_resize_callback(Some(resize_callback as fn(u32, u32)));
|
window.set_window_resize_callback(Some(resize_callback as fn(u32, u32)));
|
||||||
let _ = unsafe { window.make_current() };
|
|
||||||
|
|
||||||
println!("Pixel format of the window: {:?}", window.get_pixel_format());
|
|
||||||
|
|
||||||
let context = support::load(&window);
|
|
||||||
|
|
||||||
for event in window.wait_events() {
|
for event in window.wait_events() {
|
||||||
context.draw_frame((0.0, 1.0, 0.0, 1.0));
|
|
||||||
let _ = window.swap_buffers();
|
|
||||||
|
|
||||||
println!("{:?}", event);
|
println!("{:?}", event);
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
glutin::Event::Closed => break,
|
winit::Event::Closed => break,
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
|
|
||||||
use libc;
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
extern crate winapi;
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub type EGLNativeWindowType = winapi::HWND;
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub type EGLNativeWindowType = *const libc::c_void;
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
pub type EGLNativeWindowType = *const libc::c_void;
|
|
||||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
|
||||||
pub type EGLNativeWindowType = *const libc::c_void;
|
|
|
@ -1,731 +0,0 @@
|
||||||
#![cfg(any(target_os = "windows", target_os = "linux", target_os = "android",
|
|
||||||
target_os = "dragonfly", target_os = "freebsd"))]
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
|
|
||||||
use ContextError;
|
|
||||||
use CreationError;
|
|
||||||
use GlAttributes;
|
|
||||||
use GlContext;
|
|
||||||
use GlRequest;
|
|
||||||
use PixelFormat;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use ReleaseBehavior;
|
|
||||||
use Robustness;
|
|
||||||
use Api;
|
|
||||||
|
|
||||||
use std::ffi::{CStr, CString};
|
|
||||||
use std::os::raw::{c_void, c_int};
|
|
||||||
use std::{mem, ptr};
|
|
||||||
|
|
||||||
pub mod ffi;
|
|
||||||
|
|
||||||
/// Specifies the type of display passed as `native_display`.
|
|
||||||
pub enum NativeDisplay {
|
|
||||||
/// `None` means `EGL_DEFAULT_DISPLAY`.
|
|
||||||
X11(Option<ffi::EGLNativeDisplayType>),
|
|
||||||
/// `None` means `EGL_DEFAULT_DISPLAY`.
|
|
||||||
Gbm(Option<ffi::EGLNativeDisplayType>),
|
|
||||||
/// `None` means `EGL_DEFAULT_DISPLAY`.
|
|
||||||
Wayland(Option<ffi::EGLNativeDisplayType>),
|
|
||||||
/// `EGL_DEFAULT_DISPLAY` is mandatory for Android.
|
|
||||||
Android,
|
|
||||||
// TODO: should be `EGLDeviceEXT`
|
|
||||||
Device(ffi::EGLNativeDisplayType),
|
|
||||||
/// Don't specify any display type. Useful on windows. `None` means `EGL_DEFAULT_DISPLAY`.
|
|
||||||
Other(Option<ffi::EGLNativeDisplayType>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Context {
|
|
||||||
egl: ffi::egl::Egl,
|
|
||||||
display: ffi::egl::types::EGLDisplay,
|
|
||||||
context: ffi::egl::types::EGLContext,
|
|
||||||
surface: ffi::egl::types::EGLSurface,
|
|
||||||
api: Api,
|
|
||||||
pixel_format: PixelFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
#[inline]
|
|
||||||
fn get_native_display(egl: &ffi::egl::Egl,
|
|
||||||
native_display: NativeDisplay) -> *const c_void {
|
|
||||||
unsafe { egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
fn get_native_display(egl: &ffi::egl::Egl,
|
|
||||||
native_display: NativeDisplay) -> *const c_void {
|
|
||||||
// the first step is to query the list of extensions without any display, if supported
|
|
||||||
let dp_extensions = unsafe {
|
|
||||||
let p = egl.QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32);
|
|
||||||
|
|
||||||
// this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise
|
|
||||||
// `eglQueryString` returns an error
|
|
||||||
if p.is_null() {
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
let p = CStr::from_ptr(p);
|
|
||||||
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!(""));
|
|
||||||
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let has_dp_extension = |e: &str| dp_extensions.iter().find(|s| s == &e).is_some();
|
|
||||||
|
|
||||||
match native_display {
|
|
||||||
// Note: Some EGL implementations are missing the `eglGetPlatformDisplay(EXT)` symbol
|
|
||||||
// despite reporting `EGL_EXT_platform_base`. I'm pretty sure this is a bug.
|
|
||||||
// Therefore we detect whether the symbol is loaded in addition to checking for
|
|
||||||
// extensions.
|
|
||||||
NativeDisplay::X11(display) if has_dp_extension("EGL_KHR_platform_x11") &&
|
|
||||||
egl.GetPlatformDisplay.is_loaded() =>
|
|
||||||
{
|
|
||||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
|
||||||
// TODO: `PLATFORM_X11_SCREEN_KHR`
|
|
||||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, d as *mut _,
|
|
||||||
ptr::null()) }
|
|
||||||
},
|
|
||||||
|
|
||||||
NativeDisplay::X11(display) if has_dp_extension("EGL_EXT_platform_x11") &&
|
|
||||||
egl.GetPlatformDisplayEXT.is_loaded() =>
|
|
||||||
{
|
|
||||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
|
||||||
// TODO: `PLATFORM_X11_SCREEN_EXT`
|
|
||||||
unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, d as *mut _,
|
|
||||||
ptr::null()) }
|
|
||||||
},
|
|
||||||
|
|
||||||
NativeDisplay::Gbm(display) if has_dp_extension("EGL_KHR_platform_gbm") &&
|
|
||||||
egl.GetPlatformDisplay.is_loaded() =>
|
|
||||||
{
|
|
||||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
|
||||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, d as *mut _,
|
|
||||||
ptr::null()) }
|
|
||||||
},
|
|
||||||
|
|
||||||
NativeDisplay::Gbm(display) if has_dp_extension("EGL_MESA_platform_gbm") &&
|
|
||||||
egl.GetPlatformDisplayEXT.is_loaded() =>
|
|
||||||
{
|
|
||||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
|
||||||
unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, d as *mut _,
|
|
||||||
ptr::null()) }
|
|
||||||
},
|
|
||||||
|
|
||||||
NativeDisplay::Wayland(display) if has_dp_extension("EGL_KHR_platform_wayland") &&
|
|
||||||
egl.GetPlatformDisplay.is_loaded() =>
|
|
||||||
{
|
|
||||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
|
||||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, d as *mut _,
|
|
||||||
ptr::null()) }
|
|
||||||
},
|
|
||||||
|
|
||||||
NativeDisplay::Wayland(display) if has_dp_extension("EGL_EXT_platform_wayland") &&
|
|
||||||
egl.GetPlatformDisplayEXT.is_loaded() =>
|
|
||||||
{
|
|
||||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
|
||||||
unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_WAYLAND_EXT, d as *mut _,
|
|
||||||
ptr::null()) }
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: This will never be reached right now, as the android egl bindings
|
|
||||||
// use the static generator, so can't rely on GetPlatformDisplay(EXT).
|
|
||||||
NativeDisplay::Android if has_dp_extension("EGL_KHR_platform_android") &&
|
|
||||||
egl.GetPlatformDisplay.is_loaded() =>
|
|
||||||
{
|
|
||||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_ANDROID_KHR,
|
|
||||||
ffi::egl::DEFAULT_DISPLAY as *mut _, ptr::null()) }
|
|
||||||
},
|
|
||||||
|
|
||||||
NativeDisplay::Device(display) if has_dp_extension("EGL_EXT_platform_device") &&
|
|
||||||
egl.GetPlatformDisplay.is_loaded() =>
|
|
||||||
{
|
|
||||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_DEVICE_EXT, display as *mut _,
|
|
||||||
ptr::null()) }
|
|
||||||
},
|
|
||||||
|
|
||||||
NativeDisplay::X11(Some(display)) | NativeDisplay::Gbm(Some(display)) |
|
|
||||||
NativeDisplay::Wayland(Some(display)) | NativeDisplay::Device(display) |
|
|
||||||
NativeDisplay::Other(Some(display)) => {
|
|
||||||
unsafe { egl.GetDisplay(display as *mut _) }
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeDisplay::X11(None) | NativeDisplay::Gbm(None) | NativeDisplay::Wayland(None) |
|
|
||||||
NativeDisplay::Android | NativeDisplay::Other(None) => {
|
|
||||||
unsafe { egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) }
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
/// Start building an EGL context.
|
|
||||||
///
|
|
||||||
/// This function initializes some things and chooses the pixel format.
|
|
||||||
///
|
|
||||||
/// To finish the process, you must call `.finish(window)` on the `ContextPrototype`.
|
|
||||||
pub fn new<'a>(egl: ffi::egl::Egl, pf_reqs: &PixelFormatRequirements,
|
|
||||||
opengl: &'a GlAttributes<&'a Context>, native_display: NativeDisplay)
|
|
||||||
-> Result<ContextPrototype<'a>, CreationError>
|
|
||||||
{
|
|
||||||
if opengl.sharing.is_some() {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
// calling `eglGetDisplay` or equivalent
|
|
||||||
let display = get_native_display(&egl, native_display);
|
|
||||||
|
|
||||||
if display.is_null() {
|
|
||||||
return Err(CreationError::OsError("Could not create EGL display object".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let egl_version = unsafe {
|
|
||||||
let mut major: ffi::egl::types::EGLint = mem::uninitialized();
|
|
||||||
let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
|
|
||||||
|
|
||||||
if egl.Initialize(display, &mut major, &mut minor) == 0 {
|
|
||||||
return Err(CreationError::OsError(format!("eglInitialize failed")))
|
|
||||||
}
|
|
||||||
|
|
||||||
(major, minor)
|
|
||||||
};
|
|
||||||
|
|
||||||
// the list of extensions supported by the client once initialized is different from the
|
|
||||||
// list of extensions obtained earlier
|
|
||||||
let extensions = if egl_version >= (1, 2) {
|
|
||||||
let p = unsafe { CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)) };
|
|
||||||
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!(""));
|
|
||||||
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
};
|
|
||||||
|
|
||||||
// binding the right API and choosing the version
|
|
||||||
let (version, api) = unsafe {
|
|
||||||
match opengl.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::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
} 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::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Some(version), Api::OpenGlEs)
|
|
||||||
},
|
|
||||||
GlRequest::Specific(Api::OpenGl, version) => {
|
|
||||||
if egl_version < (1, 4) {
|
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
if egl.BindAPI(ffi::egl::OPENGL_API) == 0 {
|
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
(Some(version), Api::OpenGl)
|
|
||||||
},
|
|
||||||
GlRequest::Specific(_, _) => return Err(CreationError::OpenGlVersionNotSupported),
|
|
||||||
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::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(Some(opengles_version), Api::OpenGlEs)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (config_id, pixel_format) = unsafe {
|
|
||||||
try!(choose_fbconfig(&egl, display, &egl_version, api, version, pf_reqs))
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ContextPrototype {
|
|
||||||
opengl: opengl,
|
|
||||||
egl: egl,
|
|
||||||
display: display,
|
|
||||||
egl_version: egl_version,
|
|
||||||
extensions: extensions,
|
|
||||||
api: api,
|
|
||||||
version: version,
|
|
||||||
config_id: config_id,
|
|
||||||
pixel_format: pixel_format,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlContext for Context {
|
|
||||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
let ret = self.egl.MakeCurrent(self.display, self.surface, self.surface, self.context);
|
|
||||||
|
|
||||||
if ret == 0 {
|
|
||||||
match self.egl.GetError() as u32 {
|
|
||||||
ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost),
|
|
||||||
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_current(&self) -> bool {
|
|
||||||
unsafe { self.egl.GetCurrentContext() == self.context }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
|
||||||
let addr = addr.as_ptr();
|
|
||||||
unsafe {
|
|
||||||
self.egl.GetProcAddress(addr) as *const _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
let ret = unsafe {
|
|
||||||
self.egl.SwapBuffers(self.display, self.surface)
|
|
||||||
};
|
|
||||||
|
|
||||||
if ret == 0 {
|
|
||||||
match unsafe { self.egl.GetError() } as u32 {
|
|
||||||
ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost),
|
|
||||||
err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_api(&self) -> Api {
|
|
||||||
self.api
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
self.pixel_format.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for Context {}
|
|
||||||
unsafe impl Sync for Context {}
|
|
||||||
|
|
||||||
impl Drop for Context {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
|
||||||
// is still the current one
|
|
||||||
self.egl.DestroyContext(self.display, self.context);
|
|
||||||
self.egl.DestroySurface(self.display, self.surface);
|
|
||||||
self.egl.Terminate(self.display);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ContextPrototype<'a> {
|
|
||||||
opengl: &'a GlAttributes<&'a Context>,
|
|
||||||
egl: ffi::egl::Egl,
|
|
||||||
display: ffi::egl::types::EGLDisplay,
|
|
||||||
egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint),
|
|
||||||
extensions: Vec<String>,
|
|
||||||
api: Api,
|
|
||||||
version: Option<(u8, u8)>,
|
|
||||||
config_id: ffi::egl::types::EGLConfig,
|
|
||||||
pixel_format: PixelFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ContextPrototype<'a> {
|
|
||||||
pub fn get_native_visual_id(&self) -> ffi::egl::types::EGLint {
|
|
||||||
let mut value = unsafe { mem::uninitialized() };
|
|
||||||
let ret = unsafe { self.egl.GetConfigAttrib(self.display, self.config_id,
|
|
||||||
ffi::egl::NATIVE_VISUAL_ID
|
|
||||||
as ffi::egl::types::EGLint, &mut value) };
|
|
||||||
if ret == 0 { panic!("eglGetConfigAttrib failed") };
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish(self, native_window: ffi::EGLNativeWindowType)
|
|
||||||
-> Result<Context, CreationError>
|
|
||||||
{
|
|
||||||
let surface = unsafe {
|
|
||||||
let surface = self.egl.CreateWindowSurface(self.display, self.config_id, native_window,
|
|
||||||
ptr::null());
|
|
||||||
if surface.is_null() {
|
|
||||||
return Err(CreationError::OsError(format!("eglCreateWindowSurface failed")))
|
|
||||||
}
|
|
||||||
surface
|
|
||||||
};
|
|
||||||
|
|
||||||
self.finish_impl(surface)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish_pbuffer(self, dimensions: (u32, u32)) -> Result<Context, CreationError> {
|
|
||||||
let attrs = &[
|
|
||||||
ffi::egl::WIDTH as c_int, dimensions.0 as c_int,
|
|
||||||
ffi::egl::HEIGHT as c_int, dimensions.1 as c_int,
|
|
||||||
ffi::egl::NONE as c_int,
|
|
||||||
];
|
|
||||||
|
|
||||||
let surface = unsafe {
|
|
||||||
let surface = self.egl.CreatePbufferSurface(self.display, self.config_id,
|
|
||||||
attrs.as_ptr());
|
|
||||||
if surface.is_null() {
|
|
||||||
return Err(CreationError::OsError(format!("eglCreatePbufferSurface failed")))
|
|
||||||
}
|
|
||||||
surface
|
|
||||||
};
|
|
||||||
|
|
||||||
self.finish_impl(surface)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish_impl(self, surface: ffi::egl::types::EGLSurface)
|
|
||||||
-> Result<Context, CreationError>
|
|
||||||
{
|
|
||||||
let context = unsafe {
|
|
||||||
if let Some(version) = self.version {
|
|
||||||
try!(create_context(&self.egl, self.display, &self.egl_version,
|
|
||||||
&self.extensions, self.api, version, self.config_id,
|
|
||||||
self.opengl.debug, self.opengl.robustness))
|
|
||||||
|
|
||||||
} else if self.api == Api::OpenGlEs {
|
|
||||||
if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
|
||||||
&self.extensions, self.api, (2, 0), self.config_id,
|
|
||||||
self.opengl.debug, self.opengl.robustness)
|
|
||||||
{
|
|
||||||
ctxt
|
|
||||||
} else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
|
||||||
&self.extensions, self.api, (1, 0),
|
|
||||||
self.config_id, self.opengl.debug,
|
|
||||||
self.opengl.robustness)
|
|
||||||
{
|
|
||||||
ctxt
|
|
||||||
} else {
|
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
|
||||||
&self.extensions, self.api, (3, 2), self.config_id,
|
|
||||||
self.opengl.debug, self.opengl.robustness)
|
|
||||||
{
|
|
||||||
ctxt
|
|
||||||
} else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
|
||||||
&self.extensions, self.api, (3, 1),
|
|
||||||
self.config_id, self.opengl.debug,
|
|
||||||
self.opengl.robustness)
|
|
||||||
{
|
|
||||||
ctxt
|
|
||||||
} else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
|
||||||
&self.extensions, self.api, (1, 0),
|
|
||||||
self.config_id, self.opengl.debug,
|
|
||||||
self.opengl.robustness)
|
|
||||||
{
|
|
||||||
ctxt
|
|
||||||
} else {
|
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Context {
|
|
||||||
egl: self.egl,
|
|
||||||
display: self.display,
|
|
||||||
context: context,
|
|
||||||
surface: surface,
|
|
||||||
api: self.api,
|
|
||||||
pixel_format: self.pixel_format,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn choose_fbconfig(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)>, reqs: &PixelFormatRequirements)
|
|
||||||
-> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError>
|
|
||||||
{
|
|
||||||
let descriptor = {
|
|
||||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
|
||||||
|
|
||||||
if egl_version >= &(1, 2) {
|
|
||||||
out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
|
|
||||||
out.push(ffi::egl::RGB_BUFFER as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push(ffi::egl::SURFACE_TYPE as c_int);
|
|
||||||
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
|
|
||||||
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
|
|
||||||
out.push((ffi::egl::WINDOW_BIT) as c_int);
|
|
||||||
|
|
||||||
match (api, version) {
|
|
||||||
(Api::OpenGlEs, Some((3, _))) => {
|
|
||||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
|
||||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
|
||||||
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
|
|
||||||
out.push(ffi::egl::CONFORMANT as c_int);
|
|
||||||
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
|
|
||||||
},
|
|
||||||
(Api::OpenGlEs, Some((2, _))) => {
|
|
||||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
|
||||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
|
||||||
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
|
|
||||||
out.push(ffi::egl::CONFORMANT as c_int);
|
|
||||||
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
|
|
||||||
},
|
|
||||||
(Api::OpenGlEs, Some((1, _))) => {
|
|
||||||
if egl_version >= &(1, 3) {
|
|
||||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
|
||||||
out.push(ffi::egl::OPENGL_ES_BIT as c_int);
|
|
||||||
out.push(ffi::egl::CONFORMANT as c_int);
|
|
||||||
out.push(ffi::egl::OPENGL_ES_BIT as c_int);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(Api::OpenGlEs, _) => unimplemented!(),
|
|
||||||
(Api::OpenGl, _) => {
|
|
||||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
|
||||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
|
||||||
out.push(ffi::egl::OPENGL_BIT as c_int);
|
|
||||||
out.push(ffi::egl::CONFORMANT as c_int);
|
|
||||||
out.push(ffi::egl::OPENGL_BIT as c_int);
|
|
||||||
},
|
|
||||||
(_, _) => unimplemented!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
|
|
||||||
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
|
|
||||||
out.push(if hardware_accelerated {
|
|
||||||
ffi::egl::NONE as c_int
|
|
||||||
} else {
|
|
||||||
ffi::egl::SLOW_CONFIG as c_int
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(color) = reqs.color_bits {
|
|
||||||
out.push(ffi::egl::RED_SIZE as c_int);
|
|
||||||
out.push((color / 3) as c_int);
|
|
||||||
out.push(ffi::egl::GREEN_SIZE as c_int);
|
|
||||||
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
|
|
||||||
out.push(ffi::egl::BLUE_SIZE as c_int);
|
|
||||||
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(alpha) = reqs.alpha_bits {
|
|
||||||
out.push(ffi::egl::ALPHA_SIZE as c_int);
|
|
||||||
out.push(alpha as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(depth) = reqs.depth_bits {
|
|
||||||
out.push(ffi::egl::DEPTH_SIZE as c_int);
|
|
||||||
out.push(depth as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(stencil) = reqs.stencil_bits {
|
|
||||||
out.push(ffi::egl::STENCIL_SIZE as c_int);
|
|
||||||
out.push(stencil as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(true) = reqs.double_buffer {
|
|
||||||
return Err(CreationError::NoAvailablePixelFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(multisampling) = reqs.multisampling {
|
|
||||||
out.push(ffi::egl::SAMPLES as c_int);
|
|
||||||
out.push(multisampling as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if reqs.stereoscopy {
|
|
||||||
return Err(CreationError::NoAvailablePixelFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: srgb is not taken into account
|
|
||||||
|
|
||||||
match reqs.release_behavior {
|
|
||||||
ReleaseBehavior::Flush => (),
|
|
||||||
ReleaseBehavior::None => {
|
|
||||||
// TODO: with EGL you need to manually set the behavior
|
|
||||||
unimplemented!()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push(ffi::egl::NONE as c_int);
|
|
||||||
out
|
|
||||||
};
|
|
||||||
|
|
||||||
// calling `eglChooseConfig`
|
|
||||||
let mut config_id = mem::uninitialized();
|
|
||||||
let mut num_configs = mem::uninitialized();
|
|
||||||
if egl.ChooseConfig(display, descriptor.as_ptr(), &mut config_id, 1, &mut num_configs) == 0 {
|
|
||||||
return Err(CreationError::OsError(format!("eglChooseConfig failed")));
|
|
||||||
}
|
|
||||||
if num_configs == 0 {
|
|
||||||
return Err(CreationError::NoAvailablePixelFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
// analyzing each config
|
|
||||||
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 desc = 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((config_id, desc))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay,
|
|
||||||
egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
|
|
||||||
extensions: &[String], api: Api, version: (u8, u8),
|
|
||||||
config_id: ffi::egl::types::EGLConfig, gl_debug: bool,
|
|
||||||
gl_robustness: Robustness)
|
|
||||||
-> Result<ffi::egl::types::EGLContext, CreationError>
|
|
||||||
{
|
|
||||||
let mut context_attributes = Vec::with_capacity(10);
|
|
||||||
let mut flags = 0;
|
|
||||||
|
|
||||||
if egl_version >= &(1, 5) || extensions.iter().find(|s| s == &"EGL_KHR_create_context")
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
// handling robustness
|
|
||||||
let supports_robustness = egl_version >= &(1, 5) ||
|
|
||||||
extensions.iter()
|
|
||||||
.find(|s| s == &"EGL_EXT_create_context_robustness")
|
|
||||||
.is_some();
|
|
||||||
|
|
||||||
match gl_robustness {
|
|
||||||
Robustness::NotRobust => (),
|
|
||||||
|
|
||||||
Robustness::NoError => {
|
|
||||||
if extensions.iter().find(|s| s == &"EGL_KHR_create_context_no_error").is_some() {
|
|
||||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as c_int);
|
|
||||||
context_attributes.push(1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Robustness::RobustNoResetNotification => {
|
|
||||||
if supports_robustness {
|
|
||||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
|
||||||
as c_int);
|
|
||||||
context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as c_int);
|
|
||||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
|
||||||
} else {
|
|
||||||
return Err(CreationError::RobustnessNotSupported);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Robustness::TryRobustNoResetNotification => {
|
|
||||||
if supports_robustness {
|
|
||||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
|
||||||
as c_int);
|
|
||||||
context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as c_int);
|
|
||||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Robustness::RobustLoseContextOnReset => {
|
|
||||||
if supports_robustness {
|
|
||||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
|
||||||
as c_int);
|
|
||||||
context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int);
|
|
||||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
|
||||||
} else {
|
|
||||||
return Err(CreationError::RobustnessNotSupported);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Robustness::TryRobustLoseContextOnReset => {
|
|
||||||
if supports_robustness {
|
|
||||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
|
||||||
as c_int);
|
|
||||||
context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int);
|
|
||||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if gl_debug {
|
|
||||||
if egl_version >= &(1, 5) {
|
|
||||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
|
|
||||||
context_attributes.push(ffi::egl::TRUE as i32);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: using this flag sometimes generates an error
|
|
||||||
// there was a change in the specs that added this flag, so it may not be
|
|
||||||
// supported everywhere ; however it is not possible to know whether it is
|
|
||||||
// supported or not
|
|
||||||
//flags = flags | ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
|
|
||||||
context_attributes.push(flags);
|
|
||||||
|
|
||||||
} else if egl_version >= &(1, 3) && api == Api::OpenGlEs {
|
|
||||||
// robustness is not supported
|
|
||||||
match gl_robustness {
|
|
||||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
|
||||||
return Err(CreationError::RobustnessNotSupported);
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
match egl.GetError() as u32 {
|
|
||||||
ffi::egl::BAD_ATTRIBUTE => return Err(CreationError::OpenGlVersionNotSupported),
|
|
||||||
e => panic!("eglCreateContext failed: 0x{:x}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(context)
|
|
||||||
}
|
|
|
@ -1,498 +0,0 @@
|
||||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
|
|
||||||
|
|
||||||
use ContextError;
|
|
||||||
use CreationError;
|
|
||||||
use GlAttributes;
|
|
||||||
use GlContext;
|
|
||||||
use GlProfile;
|
|
||||||
use GlRequest;
|
|
||||||
use Api;
|
|
||||||
use PixelFormat;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use ReleaseBehavior;
|
|
||||||
use Robustness;
|
|
||||||
|
|
||||||
use libc;
|
|
||||||
use libc::c_int;
|
|
||||||
use std::ffi::{CStr, CString};
|
|
||||||
use std::{mem, ptr, slice};
|
|
||||||
|
|
||||||
use api::x11::ffi;
|
|
||||||
|
|
||||||
use platform::Window as PlatformWindow;
|
|
||||||
|
|
||||||
pub struct Context {
|
|
||||||
glx: ffi::glx::Glx,
|
|
||||||
display: *mut ffi::Display,
|
|
||||||
window: ffi::Window,
|
|
||||||
context: ffi::GLXContext,
|
|
||||||
pixel_format: PixelFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<'a>(glx: ffi::glx::Glx, xlib: &ffi::Xlib, pf_reqs: &PixelFormatRequirements,
|
|
||||||
opengl: &'a GlAttributes<&'a Context>, display: *mut ffi::Display,
|
|
||||||
screen_id: libc::c_int) -> Result<ContextPrototype<'a>, CreationError>
|
|
||||||
{
|
|
||||||
// This is completely ridiculous, but VirtualBox's OpenGL driver needs some call handled by
|
|
||||||
// *it* (i.e. not Mesa) to occur before anything else can happen. That is because
|
|
||||||
// VirtualBox's OpenGL driver is going to apply binary patches to Mesa in the DLL
|
|
||||||
// constructor and until it's loaded it won't have a chance to do that.
|
|
||||||
//
|
|
||||||
// The easiest way to do this is to just call `glXQueryVersion()` before doing anything
|
|
||||||
// else. See: https://www.virtualbox.org/ticket/8293
|
|
||||||
let (mut major, mut minor) = (0, 0);
|
|
||||||
unsafe {
|
|
||||||
glx.QueryVersion(display as *mut _, &mut major, &mut minor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// loading the list of extensions
|
|
||||||
let extensions = unsafe {
|
|
||||||
let extensions = glx.QueryExtensionsString(display as *mut _, screen_id);
|
|
||||||
let extensions = CStr::from_ptr(extensions).to_bytes().to_vec();
|
|
||||||
String::from_utf8(extensions).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
// finding the pixel format we want
|
|
||||||
let (fb_config, pixel_format) = unsafe {
|
|
||||||
try!(choose_fbconfig(&glx, &extensions, xlib, display, screen_id, pf_reqs)
|
|
||||||
.map_err(|_| CreationError::NoAvailablePixelFormat))
|
|
||||||
};
|
|
||||||
|
|
||||||
// getting the visual infos
|
|
||||||
let visual_infos: ffi::glx::types::XVisualInfo = unsafe {
|
|
||||||
let vi = glx.GetVisualFromFBConfig(display as *mut _, fb_config);
|
|
||||||
if vi.is_null() {
|
|
||||||
return Err(CreationError::OsError(format!("glxGetVisualFromFBConfig failed")));
|
|
||||||
}
|
|
||||||
let vi_copy = ptr::read(vi as *const _);
|
|
||||||
(xlib.XFree)(vi as *mut _);
|
|
||||||
vi_copy
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ContextPrototype {
|
|
||||||
glx: glx,
|
|
||||||
extensions: extensions,
|
|
||||||
opengl: opengl,
|
|
||||||
display: display,
|
|
||||||
fb_config: fb_config,
|
|
||||||
visual_infos: unsafe { mem::transmute(visual_infos) },
|
|
||||||
pixel_format: pixel_format,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlContext for Context {
|
|
||||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
// TODO: glutin needs some internal changes for proper error recovery
|
|
||||||
let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context);
|
|
||||||
if res == 0 {
|
|
||||||
panic!("glx::MakeCurrent failed");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_current(&self) -> bool {
|
|
||||||
unsafe { self.glx.GetCurrentContext() == self.context }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
|
||||||
let addr = addr.as_ptr();
|
|
||||||
unsafe {
|
|
||||||
self.glx.GetProcAddress(addr as *const _) as *const _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
// TODO: glutin needs some internal changes for proper error recovery
|
|
||||||
unsafe { self.glx.SwapBuffers(self.display as *mut _, self.window); }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_api(&self) -> ::Api {
|
|
||||||
::Api::OpenGl
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
self.pixel_format.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for Context {}
|
|
||||||
unsafe impl Sync for Context {}
|
|
||||||
|
|
||||||
impl Drop for Context {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
if self.is_current() {
|
|
||||||
self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null_mut());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.glx.DestroyContext(self.display as *mut _, self.context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ContextPrototype<'a> {
|
|
||||||
glx: ffi::glx::Glx,
|
|
||||||
extensions: String,
|
|
||||||
opengl: &'a GlAttributes<&'a Context>,
|
|
||||||
display: *mut ffi::Display,
|
|
||||||
fb_config: ffi::glx::types::GLXFBConfig,
|
|
||||||
visual_infos: ffi::XVisualInfo,
|
|
||||||
pixel_format: PixelFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ContextPrototype<'a> {
|
|
||||||
#[inline]
|
|
||||||
pub fn get_visual_infos(&self) -> &ffi::XVisualInfo {
|
|
||||||
&self.visual_infos
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish(self, window: ffi::Window) -> Result<Context, CreationError> {
|
|
||||||
let share = match self.opengl.sharing {
|
|
||||||
Some(ctxt) => ctxt.context,
|
|
||||||
None => ptr::null()
|
|
||||||
};
|
|
||||||
|
|
||||||
// loading the extra GLX functions
|
|
||||||
let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {
|
|
||||||
with_c_str(addr, |s| {
|
|
||||||
unsafe { self.glx.GetProcAddress(s as *const u8) as *const _ }
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// creating GL context
|
|
||||||
let context = match self.opengl.version {
|
|
||||||
GlRequest::Latest => {
|
|
||||||
if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &self.extensions, (3, 2),
|
|
||||||
self.opengl.profile, self.opengl.debug,
|
|
||||||
self.opengl.robustness, share,
|
|
||||||
self.display, self.fb_config, &self.visual_infos)
|
|
||||||
{
|
|
||||||
ctxt
|
|
||||||
} else if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &self.extensions,
|
|
||||||
(3, 1), self.opengl.profile,
|
|
||||||
self.opengl.debug,
|
|
||||||
self.opengl.robustness, share, self.display,
|
|
||||||
self.fb_config, &self.visual_infos)
|
|
||||||
{
|
|
||||||
ctxt
|
|
||||||
|
|
||||||
} else {
|
|
||||||
try!(create_context(&self.glx, &extra_functions, &self.extensions, (1, 0),
|
|
||||||
self.opengl.profile, self.opengl.debug,
|
|
||||||
self.opengl.robustness,
|
|
||||||
share, self.display, self.fb_config, &self.visual_infos))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
GlRequest::Specific(Api::OpenGl, (major, minor)) => {
|
|
||||||
try!(create_context(&self.glx, &extra_functions, &self.extensions, (major, minor),
|
|
||||||
self.opengl.profile, self.opengl.debug,
|
|
||||||
self.opengl.robustness, share, self.display, self.fb_config,
|
|
||||||
&self.visual_infos))
|
|
||||||
},
|
|
||||||
GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
|
|
||||||
GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
|
|
||||||
try!(create_context(&self.glx, &extra_functions, &self.extensions, (major, minor),
|
|
||||||
self.opengl.profile, self.opengl.debug,
|
|
||||||
self.opengl.robustness, share, self.display, self.fb_config,
|
|
||||||
&self.visual_infos))
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// vsync
|
|
||||||
if self.opengl.vsync {
|
|
||||||
unsafe { self.glx.MakeCurrent(self.display as *mut _, window, context) };
|
|
||||||
|
|
||||||
if extra_functions.SwapIntervalEXT.is_loaded() {
|
|
||||||
// this should be the most common extension
|
|
||||||
unsafe {
|
|
||||||
extra_functions.SwapIntervalEXT(self.display as *mut _, window, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// checking that it worked
|
|
||||||
// TODO: handle this
|
|
||||||
/*if self.builder.strict {
|
|
||||||
let mut swap = unsafe { mem::uninitialized() };
|
|
||||||
unsafe {
|
|
||||||
self.glx.QueryDrawable(self.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 self.builder.strict {
|
|
||||||
// TODO: handle this
|
|
||||||
return Err(CreationError::OsError(format!("Couldn't find any available vsync extension")));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
unsafe { self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Context {
|
|
||||||
glx: self.glx,
|
|
||||||
display: self.display,
|
|
||||||
window: window,
|
|
||||||
context: context,
|
|
||||||
pixel_format: self.pixel_format,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str,
|
|
||||||
version: (u8, u8), profile: Option<GlProfile>, debug: bool,
|
|
||||||
robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display,
|
|
||||||
fb_config: ffi::glx::types::GLXFBConfig,
|
|
||||||
visual_infos: &ffi::XVisualInfo)
|
|
||||||
-> Result<ffi::GLXContext, CreationError>
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
let context = if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context").is_some() {
|
|
||||||
let mut attributes = Vec::with_capacity(9);
|
|
||||||
|
|
||||||
attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
|
|
||||||
attributes.push(version.0 as c_int);
|
|
||||||
attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
|
|
||||||
attributes.push(version.1 as c_int);
|
|
||||||
|
|
||||||
if let Some(profile) = 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 c_int);
|
|
||||||
attributes.push(flag as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
let flags = {
|
|
||||||
let mut flags = 0;
|
|
||||||
|
|
||||||
// robustness
|
|
||||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context_robustness").is_some() {
|
|
||||||
match robustness {
|
|
||||||
Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => {
|
|
||||||
attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
|
|
||||||
attributes.push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as c_int);
|
|
||||||
flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
|
|
||||||
},
|
|
||||||
Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => {
|
|
||||||
attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
|
|
||||||
attributes.push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int);
|
|
||||||
flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
|
|
||||||
},
|
|
||||||
Robustness::NotRobust => (),
|
|
||||||
Robustness::NoError => (),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match robustness {
|
|
||||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
|
||||||
return Err(CreationError::RobustnessNotSupported);
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug {
|
|
||||||
flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int;
|
|
||||||
}
|
|
||||||
|
|
||||||
flags
|
|
||||||
};
|
|
||||||
|
|
||||||
attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as c_int);
|
|
||||||
attributes.push(flags);
|
|
||||||
|
|
||||||
attributes.push(0);
|
|
||||||
|
|
||||||
extra_functions.CreateContextAttribsARB(display as *mut _, fb_config, share, 1,
|
|
||||||
attributes.as_ptr())
|
|
||||||
|
|
||||||
} else {
|
|
||||||
let visual_infos: *const ffi::XVisualInfo = visual_infos;
|
|
||||||
glx.CreateContext(display as *mut _, visual_infos as *mut _, share, 1)
|
|
||||||
};
|
|
||||||
|
|
||||||
if context.is_null() {
|
|
||||||
// TODO: check for errors and return `OpenGlVersionNotSupported`
|
|
||||||
return Err(CreationError::OsError(format!("GL context creation failed")));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enumerates all available FBConfigs
|
|
||||||
unsafe fn choose_fbconfig(glx: &ffi::glx::Glx, extensions: &str, xlib: &ffi::Xlib,
|
|
||||||
display: *mut ffi::Display, screen_id: libc::c_int,
|
|
||||||
reqs: &PixelFormatRequirements)
|
|
||||||
-> Result<(ffi::glx::types::GLXFBConfig, PixelFormat), ()>
|
|
||||||
{
|
|
||||||
let descriptor = {
|
|
||||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
|
||||||
|
|
||||||
out.push(ffi::glx::X_RENDERABLE as c_int);
|
|
||||||
out.push(1);
|
|
||||||
|
|
||||||
out.push(ffi::glx::X_VISUAL_TYPE as c_int);
|
|
||||||
out.push(ffi::glx::TRUE_COLOR as c_int);
|
|
||||||
|
|
||||||
out.push(ffi::glx::DRAWABLE_TYPE as c_int);
|
|
||||||
out.push(ffi::glx::WINDOW_BIT as c_int);
|
|
||||||
|
|
||||||
out.push(ffi::glx::RENDER_TYPE as c_int);
|
|
||||||
if reqs.float_color_buffer {
|
|
||||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_fbconfig_float").is_some() {
|
|
||||||
out.push(ffi::glx_extra::RGBA_FLOAT_BIT_ARB as c_int);
|
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.push(ffi::glx::RGBA_BIT as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
|
|
||||||
out.push(ffi::glx::CONFIG_CAVEAT as c_int);
|
|
||||||
out.push(if hardware_accelerated {
|
|
||||||
ffi::glx::NONE as c_int
|
|
||||||
} else {
|
|
||||||
ffi::glx::SLOW_CONFIG as c_int
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(color) = reqs.color_bits {
|
|
||||||
out.push(ffi::glx::RED_SIZE as c_int);
|
|
||||||
out.push((color / 3) as c_int);
|
|
||||||
out.push(ffi::glx::GREEN_SIZE as c_int);
|
|
||||||
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
|
|
||||||
out.push(ffi::glx::BLUE_SIZE as c_int);
|
|
||||||
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(alpha) = reqs.alpha_bits {
|
|
||||||
out.push(ffi::glx::ALPHA_SIZE as c_int);
|
|
||||||
out.push(alpha as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(depth) = reqs.depth_bits {
|
|
||||||
out.push(ffi::glx::DEPTH_SIZE as c_int);
|
|
||||||
out.push(depth as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(stencil) = reqs.stencil_bits {
|
|
||||||
out.push(ffi::glx::STENCIL_SIZE as c_int);
|
|
||||||
out.push(stencil as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
let double_buffer = reqs.double_buffer.unwrap_or(true);
|
|
||||||
out.push(ffi::glx::DOUBLEBUFFER as c_int);
|
|
||||||
out.push(if double_buffer { 1 } else { 0 });
|
|
||||||
|
|
||||||
if let Some(multisampling) = reqs.multisampling {
|
|
||||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_multisample").is_some() {
|
|
||||||
out.push(ffi::glx_extra::SAMPLE_BUFFERS_ARB as c_int);
|
|
||||||
out.push(if multisampling == 0 { 0 } else { 1 });
|
|
||||||
out.push(ffi::glx_extra::SAMPLES_ARB as c_int);
|
|
||||||
out.push(multisampling as c_int);
|
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push(ffi::glx::STEREO as c_int);
|
|
||||||
out.push(if reqs.stereoscopy { 1 } else { 0 });
|
|
||||||
|
|
||||||
if reqs.srgb {
|
|
||||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_framebuffer_sRGB").is_some() {
|
|
||||||
out.push(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int);
|
|
||||||
out.push(1);
|
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match reqs.release_behavior {
|
|
||||||
ReleaseBehavior::Flush => (),
|
|
||||||
ReleaseBehavior::None => {
|
|
||||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_context_flush_control").is_some() {
|
|
||||||
out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int);
|
|
||||||
out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push(0);
|
|
||||||
out
|
|
||||||
};
|
|
||||||
|
|
||||||
// calling glXChooseFBConfig
|
|
||||||
let fb_config = {
|
|
||||||
let mut num_configs = 1;
|
|
||||||
let result = glx.ChooseFBConfig(display as *mut _, screen_id, descriptor.as_ptr(),
|
|
||||||
&mut num_configs);
|
|
||||||
if result.is_null() { return Err(()); }
|
|
||||||
if num_configs == 0 { return Err(()); }
|
|
||||||
let val = *result;
|
|
||||||
(xlib.XFree)(result as *mut _);
|
|
||||||
val
|
|
||||||
};
|
|
||||||
|
|
||||||
let get_attrib = |attrib: c_int| -> i32 {
|
|
||||||
let mut value = 0;
|
|
||||||
glx.GetFBConfigAttrib(display as *mut _, fb_config, attrib, &mut value);
|
|
||||||
// TODO: check return value
|
|
||||||
value
|
|
||||||
};
|
|
||||||
|
|
||||||
let pf_desc = PixelFormat {
|
|
||||||
hardware_accelerated: get_attrib(ffi::glx::CONFIG_CAVEAT as c_int) !=
|
|
||||||
ffi::glx::SLOW_CONFIG as c_int,
|
|
||||||
color_bits: get_attrib(ffi::glx::RED_SIZE as c_int) as u8 +
|
|
||||||
get_attrib(ffi::glx::GREEN_SIZE as c_int) as u8 +
|
|
||||||
get_attrib(ffi::glx::BLUE_SIZE as c_int) as u8,
|
|
||||||
alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as c_int) as u8,
|
|
||||||
depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as c_int) as u8,
|
|
||||||
stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as c_int) as u8,
|
|
||||||
stereoscopy: get_attrib(ffi::glx::STEREO as c_int) != 0,
|
|
||||||
double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as c_int) != 0,
|
|
||||||
multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as c_int) != 0 {
|
|
||||||
Some(get_attrib(ffi::glx::SAMPLES as c_int) as u16)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((fb_config, pf_desc))
|
|
||||||
}
|
|
|
@ -2,12 +2,8 @@ pub mod android;
|
||||||
pub mod caca;
|
pub mod caca;
|
||||||
pub mod cocoa;
|
pub mod cocoa;
|
||||||
pub mod dlopen;
|
pub mod dlopen;
|
||||||
pub mod egl;
|
|
||||||
pub mod emscripten;
|
pub mod emscripten;
|
||||||
pub mod glx;
|
|
||||||
pub mod osmesa;
|
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
pub mod wgl;
|
|
||||||
pub mod win32;
|
pub mod win32;
|
||||||
pub mod x11;
|
pub mod x11;
|
||||||
pub mod ios;
|
pub mod ios;
|
||||||
|
|
|
@ -1,140 +0,0 @@
|
||||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
|
|
||||||
|
|
||||||
extern crate osmesa_sys;
|
|
||||||
|
|
||||||
use Api;
|
|
||||||
use ContextError;
|
|
||||||
use CreationError;
|
|
||||||
use GlAttributes;
|
|
||||||
use GlContext;
|
|
||||||
use PixelFormat;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use Robustness;
|
|
||||||
use libc;
|
|
||||||
use std::{mem, ptr};
|
|
||||||
use std::ffi::CString;
|
|
||||||
|
|
||||||
pub struct OsMesaContext {
|
|
||||||
context: osmesa_sys::OSMesaContext,
|
|
||||||
buffer: Vec<u32>,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum OsMesaCreationError {
|
|
||||||
CreationError(CreationError),
|
|
||||||
NotSupported,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CreationError> for OsMesaCreationError {
|
|
||||||
#[inline]
|
|
||||||
fn from(e: CreationError) -> OsMesaCreationError {
|
|
||||||
OsMesaCreationError::CreationError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OsMesaContext {
|
|
||||||
pub fn new(dimensions: (u32, u32), pf_reqs: &PixelFormatRequirements,
|
|
||||||
opengl: &GlAttributes<&OsMesaContext>) -> Result<OsMesaContext, OsMesaCreationError>
|
|
||||||
{
|
|
||||||
if let Err(_) = osmesa_sys::OsMesa::try_loading() {
|
|
||||||
return Err(OsMesaCreationError::NotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
if opengl.sharing.is_some() { unimplemented!() } // TODO: proper error
|
|
||||||
|
|
||||||
match opengl.robustness {
|
|
||||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
|
||||||
return Err(CreationError::RobustnessNotSupported.into());
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: use `pf_reqs` for the format
|
|
||||||
// TODO: check OpenGL version and return `OpenGlVersionNotSupported` if necessary
|
|
||||||
|
|
||||||
Ok(OsMesaContext {
|
|
||||||
width: dimensions.0,
|
|
||||||
height: dimensions.1,
|
|
||||||
buffer: ::std::iter::repeat(unsafe { mem::uninitialized() })
|
|
||||||
.take((dimensions.0 * dimensions.1) as usize).collect(),
|
|
||||||
context: unsafe {
|
|
||||||
let ctxt = osmesa_sys::OSMesaCreateContext(0x1908, ptr::null_mut());
|
|
||||||
if ctxt.is_null() {
|
|
||||||
return Err(CreationError::OsError("OSMesaCreateContext failed".to_string()).into());
|
|
||||||
}
|
|
||||||
ctxt
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_framebuffer(&self) -> &[u32] {
|
|
||||||
&self.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
|
||||||
(self.width, self.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
// TODO: can we remove this without causing havoc?
|
|
||||||
#[inline]
|
|
||||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlContext for OsMesaContext {
|
|
||||||
#[inline]
|
|
||||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
let ret = osmesa_sys::OSMesaMakeCurrent(self.context, self.buffer.as_ptr()
|
|
||||||
as *mut _, 0x1401, self.width
|
|
||||||
as libc::c_int, self.height as libc::c_int);
|
|
||||||
|
|
||||||
// an error can only happen in case of invalid parameter, which would indicate a bug
|
|
||||||
// in glutin
|
|
||||||
if ret == 0 {
|
|
||||||
panic!("OSMesaMakeCurrent failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_current(&self) -> bool {
|
|
||||||
unsafe { osmesa_sys::OSMesaGetCurrentContext() == self.context }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
unsafe {
|
|
||||||
let c_str = CString::new(addr.as_bytes().to_vec()).unwrap();
|
|
||||||
mem::transmute(osmesa_sys::OSMesaGetProcAddress(mem::transmute(c_str.as_ptr())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_api(&self) -> Api {
|
|
||||||
Api::OpenGl
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for OsMesaContext {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { osmesa_sys::OSMesaDestroyContext(self.context) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for OsMesaContext {}
|
|
||||||
unsafe impl Sync for OsMesaContext {}
|
|
|
@ -1,12 +0,0 @@
|
||||||
/// WGL bindings
|
|
||||||
pub mod wgl {
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/wgl_bindings.rs"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Functions that are not necessarly always available
|
|
||||||
pub mod wgl_extra {
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/wgl_extra_bindings.rs"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[link(name = "opengl32")]
|
|
||||||
extern {}
|
|
|
@ -1,47 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::os::raw::c_void;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use winapi;
|
|
||||||
use CreationError;
|
|
||||||
|
|
||||||
use super::gl;
|
|
||||||
/// A guard for when you want to make the context current. Destroying the guard restores the
|
|
||||||
/// previously-current context.
|
|
||||||
pub struct CurrentContextGuard<'a, 'b> {
|
|
||||||
previous_hdc: winapi::HDC,
|
|
||||||
previous_hglrc: winapi::HGLRC,
|
|
||||||
marker1: PhantomData<&'a ()>,
|
|
||||||
marker2: PhantomData<&'b ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> CurrentContextGuard<'a, 'b> {
|
|
||||||
pub unsafe fn make_current(hdc: winapi::HDC, context: winapi::HGLRC)
|
|
||||||
-> Result<CurrentContextGuard<'a, 'b>, CreationError>
|
|
||||||
{
|
|
||||||
let previous_hdc = gl::wgl::GetCurrentDC() as winapi::HDC;
|
|
||||||
let previous_hglrc = gl::wgl::GetCurrentContext() as winapi::HGLRC;
|
|
||||||
|
|
||||||
let result = gl::wgl::MakeCurrent(hdc as *const _, context as *const _);
|
|
||||||
if result == 0 {
|
|
||||||
return Err(CreationError::OsError(format!("wglMakeCurrent function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(CurrentContextGuard {
|
|
||||||
previous_hdc: previous_hdc,
|
|
||||||
previous_hglrc: previous_hglrc,
|
|
||||||
marker1: PhantomData,
|
|
||||||
marker2: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gl::wgl::MakeCurrent(self.previous_hdc as *const c_void,
|
|
||||||
self.previous_hglrc as *const c_void);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,787 +0,0 @@
|
||||||
#![cfg(any(target_os = "windows"))]
|
|
||||||
|
|
||||||
use ContextError;
|
|
||||||
use CreationError;
|
|
||||||
use GlAttributes;
|
|
||||||
use GlContext;
|
|
||||||
use GlRequest;
|
|
||||||
use GlProfile;
|
|
||||||
use PixelFormat;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use ReleaseBehavior;
|
|
||||||
use Robustness;
|
|
||||||
use Api;
|
|
||||||
|
|
||||||
use self::make_current_guard::CurrentContextGuard;
|
|
||||||
|
|
||||||
use std::ffi::{CStr, CString, OsStr};
|
|
||||||
use std::os::raw::{c_void, c_int};
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
|
||||||
use std::{mem, ptr};
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use winapi;
|
|
||||||
use kernel32;
|
|
||||||
use user32;
|
|
||||||
use gdi32;
|
|
||||||
|
|
||||||
mod make_current_guard;
|
|
||||||
mod gl;
|
|
||||||
|
|
||||||
/// A WGL context.
|
|
||||||
///
|
|
||||||
/// Note: should be destroyed before its window.
|
|
||||||
pub struct Context {
|
|
||||||
context: ContextWrapper,
|
|
||||||
|
|
||||||
hdc: winapi::HDC,
|
|
||||||
|
|
||||||
/// Binded to `opengl32.dll`.
|
|
||||||
///
|
|
||||||
/// `wglGetProcAddress` returns null for GL 1.1 functions because they are
|
|
||||||
/// already defined by the system. This module contains them.
|
|
||||||
gl_library: winapi::HMODULE,
|
|
||||||
|
|
||||||
/// The pixel format that has been used to create this context.
|
|
||||||
pixel_format: PixelFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A simple wrapper that destroys the window when it is destroyed.
|
|
||||||
struct WindowWrapper(winapi::HWND, winapi::HDC);
|
|
||||||
|
|
||||||
impl Drop for WindowWrapper {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
user32::DestroyWindow(self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wraps around a context so that it is destroyed when necessary.
|
|
||||||
struct ContextWrapper(winapi::HGLRC);
|
|
||||||
|
|
||||||
impl Drop for ContextWrapper {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gl::wgl::DeleteContext(self.0 as *const _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
/// Attempt to build a new WGL context on a window.
|
|
||||||
///
|
|
||||||
/// The window must **not** have had `SetPixelFormat` called on it.
|
|
||||||
///
|
|
||||||
/// # Unsafety
|
|
||||||
///
|
|
||||||
/// The `window` must continue to exist as long as the resulting `Context` exists.
|
|
||||||
pub unsafe fn new(pf_reqs: &PixelFormatRequirements, opengl: &GlAttributes<winapi::HGLRC>,
|
|
||||||
window: winapi::HWND) -> Result<Context, CreationError>
|
|
||||||
{
|
|
||||||
let hdc = user32::GetDC(window);
|
|
||||||
if hdc.is_null() {
|
|
||||||
let err = Err(CreationError::OsError(format!("GetDC function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// loading the functions that are not guaranteed to be supported
|
|
||||||
let extra_functions = try!(load_extra_functions(window));
|
|
||||||
|
|
||||||
// getting the list of the supported extensions
|
|
||||||
let extensions = if extra_functions.GetExtensionsStringARB.is_loaded() {
|
|
||||||
let data = extra_functions.GetExtensionsStringARB(hdc as *const _);
|
|
||||||
let data = CStr::from_ptr(data).to_bytes().to_vec();
|
|
||||||
String::from_utf8(data).unwrap()
|
|
||||||
|
|
||||||
} else if extra_functions.GetExtensionsStringEXT.is_loaded() {
|
|
||||||
let data = extra_functions.GetExtensionsStringEXT();
|
|
||||||
let data = CStr::from_ptr(data).to_bytes().to_vec();
|
|
||||||
String::from_utf8(data).unwrap()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
format!("")
|
|
||||||
};
|
|
||||||
|
|
||||||
// calling SetPixelFormat
|
|
||||||
let pixel_format = {
|
|
||||||
let (id, f) = if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format")
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
try!(choose_arb_pixel_format(&extra_functions, &extensions, hdc, pf_reqs)
|
|
||||||
.map_err(|_| CreationError::NoAvailablePixelFormat))
|
|
||||||
} else {
|
|
||||||
try!(choose_native_pixel_format(hdc, pf_reqs)
|
|
||||||
.map_err(|_| CreationError::NoAvailablePixelFormat))
|
|
||||||
};
|
|
||||||
|
|
||||||
try!(set_pixel_format(hdc, id));
|
|
||||||
f
|
|
||||||
};
|
|
||||||
|
|
||||||
// creating the OpenGL context
|
|
||||||
let context = try!(create_context(Some((&extra_functions, pf_reqs, opengl, &extensions)),
|
|
||||||
window, hdc));
|
|
||||||
|
|
||||||
// loading the opengl32 module
|
|
||||||
let gl_library = try!(load_opengl32_dll());
|
|
||||||
|
|
||||||
// handling vsync
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() {
|
|
||||||
let _guard = try!(CurrentContextGuard::make_current(hdc, context.0));
|
|
||||||
|
|
||||||
if extra_functions.SwapIntervalEXT(if opengl.vsync { 1 } else { 0 }) == 0 {
|
|
||||||
return Err(CreationError::OsError(format!("wglSwapIntervalEXT failed")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Context {
|
|
||||||
context: context,
|
|
||||||
hdc: hdc,
|
|
||||||
gl_library: gl_library,
|
|
||||||
pixel_format: pixel_format,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the raw HGLRC.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_hglrc(&self) -> winapi::HGLRC {
|
|
||||||
self.context.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlContext for Context {
|
|
||||||
#[inline]
|
|
||||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
if gl::wgl::MakeCurrent(self.hdc as *const _, self.context.0 as *const _) != 0 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ContextError::IoError(io::Error::last_os_error()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_current(&self) -> bool {
|
|
||||||
unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const c_void }
|
|
||||||
}
|
|
||||||
|
|
||||||
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 _
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
// TODO: decide how to handle the error
|
|
||||||
/*if unsafe { gdi32::SwapBuffers(self.hdc) } != 0 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(ContextError::IoError(io::Error::last_os_error()))
|
|
||||||
}*/
|
|
||||||
unsafe { gdi32::SwapBuffers(self.hdc) };
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_api(&self) -> Api {
|
|
||||||
// FIXME: can be opengl es
|
|
||||||
Api::OpenGl
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
self.pixel_format.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for Context {}
|
|
||||||
unsafe impl Sync for Context {}
|
|
||||||
|
|
||||||
/// Creates an OpenGL context.
|
|
||||||
///
|
|
||||||
/// If `extra` is `Some`, this function will attempt to use the latest WGL functions to create the
|
|
||||||
/// context.
|
|
||||||
///
|
|
||||||
/// Otherwise, only the basic API will be used and the chances of `CreationError::NotSupported`
|
|
||||||
/// being returned increase.
|
|
||||||
unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &PixelFormatRequirements,
|
|
||||||
&GlAttributes<winapi::HGLRC>, &str)>,
|
|
||||||
_: winapi::HWND, hdc: winapi::HDC)
|
|
||||||
-> Result<ContextWrapper, CreationError>
|
|
||||||
{
|
|
||||||
let share;
|
|
||||||
|
|
||||||
if let Some((extra_functions, pf_reqs, opengl, extensions)) = extra {
|
|
||||||
share = opengl.sharing.unwrap_or(ptr::null_mut());
|
|
||||||
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context").is_some() {
|
|
||||||
let mut attributes = Vec::new();
|
|
||||||
|
|
||||||
match opengl.version {
|
|
||||||
GlRequest::Latest => {},
|
|
||||||
GlRequest::Specific(Api::OpenGl, (major, minor)) => {
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
|
|
||||||
attributes.push(major as c_int);
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
|
|
||||||
attributes.push(minor as c_int);
|
|
||||||
},
|
|
||||||
GlRequest::Specific(Api::OpenGlEs, (major, minor)) => {
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_EXT_create_context_es2_profile")
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT as c_int);
|
|
||||||
} else {
|
|
||||||
return Err(CreationError::OpenGlVersionNotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
|
|
||||||
attributes.push(major as c_int);
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
|
|
||||||
attributes.push(minor as c_int);
|
|
||||||
},
|
|
||||||
GlRequest::Specific(_, _) => return Err(CreationError::OpenGlVersionNotSupported),
|
|
||||||
GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
|
|
||||||
attributes.push(major as c_int);
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
|
|
||||||
attributes.push(minor as c_int);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(profile) = opengl.profile {
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_profile").is_some()
|
|
||||||
{
|
|
||||||
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 c_int);
|
|
||||||
attributes.push(flag as c_int);
|
|
||||||
} else {
|
|
||||||
return Err(CreationError::NotSupported);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let flags = {
|
|
||||||
let mut flags = 0;
|
|
||||||
|
|
||||||
// robustness
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_robustness").is_some() {
|
|
||||||
match opengl.robustness {
|
|
||||||
Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => {
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
|
|
||||||
attributes.push(gl::wgl_extra::NO_RESET_NOTIFICATION_ARB as c_int);
|
|
||||||
flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
|
|
||||||
},
|
|
||||||
Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => {
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
|
|
||||||
attributes.push(gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int);
|
|
||||||
flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
|
|
||||||
},
|
|
||||||
Robustness::NotRobust => (),
|
|
||||||
Robustness::NoError => (),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match opengl.robustness {
|
|
||||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
|
||||||
return Err(CreationError::RobustnessNotSupported);
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opengl.debug {
|
|
||||||
flags = flags | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as c_int;
|
|
||||||
}
|
|
||||||
|
|
||||||
flags
|
|
||||||
};
|
|
||||||
|
|
||||||
attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as c_int);
|
|
||||||
attributes.push(flags);
|
|
||||||
|
|
||||||
attributes.push(0);
|
|
||||||
|
|
||||||
let ctxt = extra_functions.CreateContextAttribsARB(hdc as *const c_void,
|
|
||||||
share as *const c_void,
|
|
||||||
attributes.as_ptr());
|
|
||||||
|
|
||||||
if ctxt.is_null() {
|
|
||||||
return Err(CreationError::OsError(format!("wglCreateContextAttribsARB failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
} else {
|
|
||||||
return Ok(ContextWrapper(ctxt as winapi::HGLRC));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
share = ptr::null_mut();
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctxt = gl::wgl::CreateContext(hdc as *const c_void);
|
|
||||||
if ctxt.is_null() {
|
|
||||||
return Err(CreationError::OsError(format!("wglCreateContext failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !share.is_null() {
|
|
||||||
if gl::wgl::ShareLists(share as *const c_void, ctxt) == 0 {
|
|
||||||
return Err(CreationError::OsError(format!("wglShareLists failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ContextWrapper(ctxt as winapi::HGLRC))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Chooses a pixel formats without using WGL.
|
|
||||||
///
|
|
||||||
/// Gives less precise results than `enumerate_arb_pixel_formats`.
|
|
||||||
unsafe fn choose_native_pixel_format(hdc: winapi::HDC, reqs: &PixelFormatRequirements)
|
|
||||||
-> Result<(c_int, PixelFormat), ()>
|
|
||||||
{
|
|
||||||
// TODO: hardware acceleration is not handled
|
|
||||||
|
|
||||||
// handling non-supported stuff
|
|
||||||
if reqs.float_color_buffer {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match reqs.multisampling {
|
|
||||||
Some(0) => (),
|
|
||||||
None => (),
|
|
||||||
Some(_) => return Err(())
|
|
||||||
};
|
|
||||||
|
|
||||||
if reqs.stereoscopy {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if reqs.srgb {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if reqs.release_behavior != ReleaseBehavior::Flush {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// building the descriptor to pass to ChoosePixelFormat
|
|
||||||
let descriptor = winapi::PIXELFORMATDESCRIPTOR {
|
|
||||||
nSize: mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() as u16,
|
|
||||||
nVersion: 1,
|
|
||||||
dwFlags: {
|
|
||||||
let f1 = match reqs.double_buffer {
|
|
||||||
None => winapi::PFD_DOUBLEBUFFER, // Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose
|
|
||||||
Some(true) => winapi::PFD_DOUBLEBUFFER,
|
|
||||||
Some(false) => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let f2 = if reqs.stereoscopy {
|
|
||||||
winapi::PFD_STEREO
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_SUPPORT_OPENGL | f1 | f2
|
|
||||||
},
|
|
||||||
iPixelType: winapi::PFD_TYPE_RGBA,
|
|
||||||
cColorBits: reqs.color_bits.unwrap_or(0),
|
|
||||||
cRedBits: 0,
|
|
||||||
cRedShift: 0,
|
|
||||||
cGreenBits: 0,
|
|
||||||
cGreenShift: 0,
|
|
||||||
cBlueBits: 0,
|
|
||||||
cBlueShift: 0,
|
|
||||||
cAlphaBits: reqs.alpha_bits.unwrap_or(0),
|
|
||||||
cAlphaShift: 0,
|
|
||||||
cAccumBits: 0,
|
|
||||||
cAccumRedBits: 0,
|
|
||||||
cAccumGreenBits: 0,
|
|
||||||
cAccumBlueBits: 0,
|
|
||||||
cAccumAlphaBits: 0,
|
|
||||||
cDepthBits: reqs.depth_bits.unwrap_or(0),
|
|
||||||
cStencilBits: reqs.stencil_bits.unwrap_or(0),
|
|
||||||
cAuxBuffers: 0,
|
|
||||||
iLayerType: winapi::PFD_MAIN_PLANE,
|
|
||||||
bReserved: 0,
|
|
||||||
dwLayerMask: 0,
|
|
||||||
dwVisibleMask: 0,
|
|
||||||
dwDamageMask: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// now querying
|
|
||||||
let pf_id = gdi32::ChoosePixelFormat(hdc, &descriptor);
|
|
||||||
if pf_id == 0 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// querying back the capabilities of what windows told us
|
|
||||||
let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed();
|
|
||||||
if gdi32::DescribePixelFormat(hdc, pf_id, mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() as u32,
|
|
||||||
&mut output) == 0
|
|
||||||
{
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// windows may return us a non-conforming pixel format if none are supported, so we have to
|
|
||||||
// check this
|
|
||||||
if (output.dwFlags & winapi::PFD_DRAW_TO_WINDOW) == 0 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
if (output.dwFlags & winapi::PFD_SUPPORT_OPENGL) == 0 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
if output.iPixelType != winapi::PFD_TYPE_RGBA {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let pf_desc = PixelFormat {
|
|
||||||
hardware_accelerated: (output.dwFlags & winapi::PFD_GENERIC_FORMAT) == 0,
|
|
||||||
color_bits: output.cRedBits + output.cGreenBits + output.cBlueBits,
|
|
||||||
alpha_bits: output.cAlphaBits,
|
|
||||||
depth_bits: output.cDepthBits,
|
|
||||||
stencil_bits: output.cStencilBits,
|
|
||||||
stereoscopy: (output.dwFlags & winapi::PFD_STEREO) != 0,
|
|
||||||
double_buffer: (output.dwFlags & winapi::PFD_DOUBLEBUFFER) != 0,
|
|
||||||
multisampling: None,
|
|
||||||
srgb: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if pf_desc.alpha_bits < reqs.alpha_bits.unwrap_or(0) {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
if pf_desc.depth_bits < reqs.depth_bits.unwrap_or(0) {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
if pf_desc.stencil_bits < reqs.stencil_bits.unwrap_or(0) {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
if pf_desc.color_bits < reqs.color_bits.unwrap_or(0) {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
if let Some(req) = reqs.hardware_accelerated {
|
|
||||||
if pf_desc.hardware_accelerated != req {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(req) = reqs.double_buffer {
|
|
||||||
if pf_desc.double_buffer != req {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((pf_id, pf_desc))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enumerates the list of pixel formats by using extra WGL functions.
|
|
||||||
///
|
|
||||||
/// Gives more precise results than `enumerate_native_pixel_formats`.
|
|
||||||
unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str,
|
|
||||||
hdc: winapi::HDC, reqs: &PixelFormatRequirements)
|
|
||||||
-> Result<(c_int, PixelFormat), ()>
|
|
||||||
{
|
|
||||||
let descriptor = {
|
|
||||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
|
||||||
|
|
||||||
out.push(gl::wgl_extra::DRAW_TO_WINDOW_ARB as c_int);
|
|
||||||
out.push(1);
|
|
||||||
|
|
||||||
out.push(gl::wgl_extra::SUPPORT_OPENGL_ARB as c_int);
|
|
||||||
out.push(1);
|
|
||||||
|
|
||||||
out.push(gl::wgl_extra::PIXEL_TYPE_ARB as c_int);
|
|
||||||
if reqs.float_color_buffer {
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format_float").is_some() {
|
|
||||||
out.push(gl::wgl_extra::TYPE_RGBA_FLOAT_ARB as c_int);
|
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.push(gl::wgl_extra::TYPE_RGBA_ARB as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
|
|
||||||
out.push(gl::wgl_extra::ACCELERATION_ARB as c_int);
|
|
||||||
out.push(if hardware_accelerated {
|
|
||||||
gl::wgl_extra::FULL_ACCELERATION_ARB as c_int
|
|
||||||
} else {
|
|
||||||
gl::wgl_extra::NO_ACCELERATION_ARB as c_int
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(color) = reqs.color_bits {
|
|
||||||
out.push(gl::wgl_extra::COLOR_BITS_ARB as c_int);
|
|
||||||
out.push(color as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(alpha) = reqs.alpha_bits {
|
|
||||||
out.push(gl::wgl_extra::ALPHA_BITS_ARB as c_int);
|
|
||||||
out.push(alpha as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(depth) = reqs.depth_bits {
|
|
||||||
out.push(gl::wgl_extra::DEPTH_BITS_ARB as c_int);
|
|
||||||
out.push(depth as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(stencil) = reqs.stencil_bits {
|
|
||||||
out.push(gl::wgl_extra::STENCIL_BITS_ARB as c_int);
|
|
||||||
out.push(stencil as c_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer double buffering if unspecified (probably shouldn't once you can choose)
|
|
||||||
let double_buffer = reqs.double_buffer.unwrap_or(true);
|
|
||||||
out.push(gl::wgl_extra::DOUBLE_BUFFER_ARB as c_int);
|
|
||||||
out.push(if double_buffer { 1 } else { 0 });
|
|
||||||
|
|
||||||
if let Some(multisampling) = reqs.multisampling {
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() {
|
|
||||||
out.push(gl::wgl_extra::SAMPLE_BUFFERS_ARB as c_int);
|
|
||||||
out.push(if multisampling == 0 { 0 } else { 1 });
|
|
||||||
out.push(gl::wgl_extra::SAMPLES_ARB as c_int);
|
|
||||||
out.push(multisampling as c_int);
|
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push(gl::wgl_extra::STEREO_ARB as c_int);
|
|
||||||
out.push(if reqs.stereoscopy { 1 } else { 0 });
|
|
||||||
|
|
||||||
if reqs.srgb {
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() {
|
|
||||||
out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int);
|
|
||||||
out.push(1);
|
|
||||||
} else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() {
|
|
||||||
out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int);
|
|
||||||
out.push(1);
|
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match reqs.release_behavior {
|
|
||||||
ReleaseBehavior::Flush => (),
|
|
||||||
ReleaseBehavior::None => {
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_context_flush_control").is_some() {
|
|
||||||
out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int);
|
|
||||||
out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push(0);
|
|
||||||
out
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut format_id = mem::uninitialized();
|
|
||||||
let mut num_formats = mem::uninitialized();
|
|
||||||
if extra.ChoosePixelFormatARB(hdc as *const _, descriptor.as_ptr(), ptr::null(), 1,
|
|
||||||
&mut format_id, &mut num_formats) == 0
|
|
||||||
{
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if num_formats == 0 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let get_info = |attrib: u32| {
|
|
||||||
let mut value = mem::uninitialized();
|
|
||||||
extra.GetPixelFormatAttribivARB(hdc as *const _, format_id as c_int,
|
|
||||||
0, 1, [attrib as c_int].as_ptr(),
|
|
||||||
&mut value);
|
|
||||||
value as u32
|
|
||||||
};
|
|
||||||
|
|
||||||
let pf_desc = PixelFormat {
|
|
||||||
hardware_accelerated: get_info(gl::wgl_extra::ACCELERATION_ARB) !=
|
|
||||||
gl::wgl_extra::NO_ACCELERATION_ARB,
|
|
||||||
color_bits: get_info(gl::wgl_extra::RED_BITS_ARB) as u8 +
|
|
||||||
get_info(gl::wgl_extra::GREEN_BITS_ARB) as u8 +
|
|
||||||
get_info(gl::wgl_extra::BLUE_BITS_ARB) as u8,
|
|
||||||
alpha_bits: get_info(gl::wgl_extra::ALPHA_BITS_ARB) as u8,
|
|
||||||
depth_bits: get_info(gl::wgl_extra::DEPTH_BITS_ARB) as u8,
|
|
||||||
stencil_bits: get_info(gl::wgl_extra::STENCIL_BITS_ARB) as u8,
|
|
||||||
stereoscopy: get_info(gl::wgl_extra::STEREO_ARB) != 0,
|
|
||||||
double_buffer: get_info(gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0,
|
|
||||||
multisampling: {
|
|
||||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() {
|
|
||||||
match get_info(gl::wgl_extra::SAMPLES_ARB) {
|
|
||||||
0 => None,
|
|
||||||
a => Some(a as u16),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
srgb: if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() {
|
|
||||||
get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0
|
|
||||||
} else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() {
|
|
||||||
get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((format_id, pf_desc))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls `SetPixelFormat` on a window.
|
|
||||||
unsafe fn set_pixel_format(hdc: winapi::HDC, id: c_int) -> Result<(), CreationError> {
|
|
||||||
let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed();
|
|
||||||
|
|
||||||
if gdi32::DescribePixelFormat(hdc, id, mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>()
|
|
||||||
as winapi::UINT, &mut output) == 0
|
|
||||||
{
|
|
||||||
return Err(CreationError::OsError(format!("DescribePixelFormat function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
if gdi32::SetPixelFormat(hdc, id, &output) == 0 {
|
|
||||||
return Err(CreationError::OsError(format!("SetPixelFormat function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads the `opengl32.dll` library.
|
|
||||||
unsafe fn load_opengl32_dll() -> Result<winapi::HMODULE, CreationError> {
|
|
||||||
let name = OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let lib = kernel32::LoadLibraryW(name.as_ptr());
|
|
||||||
|
|
||||||
if lib.is_null() {
|
|
||||||
return Err(CreationError::OsError(format!("LoadLibrary function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(lib)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads the WGL functions that are not guaranteed to be supported.
|
|
||||||
///
|
|
||||||
/// The `window` must be passed because the driver can vary depending on the window's
|
|
||||||
/// characteristics.
|
|
||||||
unsafe fn load_extra_functions(window: winapi::HWND) -> Result<gl::wgl_extra::Wgl, CreationError> {
|
|
||||||
let (ex_style, style) = (winapi::WS_EX_APPWINDOW, winapi::WS_POPUP |
|
|
||||||
winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN);
|
|
||||||
|
|
||||||
// creating a dummy invisible window
|
|
||||||
let dummy_window = {
|
|
||||||
// getting the rect of the real window
|
|
||||||
let rect = {
|
|
||||||
let mut placement: winapi::WINDOWPLACEMENT = mem::zeroed();
|
|
||||||
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT;
|
|
||||||
if user32::GetWindowPlacement(window, &mut placement) == 0 {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
placement.rcNormalPosition
|
|
||||||
};
|
|
||||||
|
|
||||||
// getting the class name of the real window
|
|
||||||
let mut class_name = [0u16; 128];
|
|
||||||
if user32::GetClassNameW(window, class_name.as_mut_ptr(), 128) == 0 {
|
|
||||||
return Err(CreationError::OsError(format!("GetClassNameW function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
// this dummy window should match the real one enough to get the same OpenGL driver
|
|
||||||
let win = user32::CreateWindowExW(ex_style, class_name.as_ptr(),
|
|
||||||
b"dummy window\0".as_ptr() as *const _, style,
|
|
||||||
winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT,
|
|
||||||
rect.right - rect.left,
|
|
||||||
rect.bottom - rect.top,
|
|
||||||
ptr::null_mut(), ptr::null_mut(),
|
|
||||||
kernel32::GetModuleHandleW(ptr::null()),
|
|
||||||
ptr::null_mut());
|
|
||||||
|
|
||||||
if win.is_null() {
|
|
||||||
return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
let hdc = user32::GetDC(win);
|
|
||||||
if hdc.is_null() {
|
|
||||||
let err = Err(CreationError::OsError(format!("GetDC function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowWrapper(win, hdc)
|
|
||||||
};
|
|
||||||
|
|
||||||
// getting the pixel format that we will use and setting it
|
|
||||||
{
|
|
||||||
let id = try!(choose_dummy_pixel_format(dummy_window.1));
|
|
||||||
try!(set_pixel_format(dummy_window.1, id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// creating the dummy OpenGL context and making it current
|
|
||||||
let dummy_context = try!(create_context(None, dummy_window.0, dummy_window.1));
|
|
||||||
let _current_context = try!(CurrentContextGuard::make_current(dummy_window.1,
|
|
||||||
dummy_context.0));
|
|
||||||
|
|
||||||
// loading the extra WGL functions
|
|
||||||
Ok(gl::wgl_extra::Wgl::load_with(|addr| {
|
|
||||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
|
||||||
let addr = addr.as_ptr();
|
|
||||||
gl::wgl::GetProcAddress(addr) as *const c_void
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function chooses a pixel format that is likely to be provided by
|
|
||||||
/// the main video driver of the system.
|
|
||||||
fn choose_dummy_pixel_format(hdc: winapi::HDC) -> Result<c_int, CreationError> {
|
|
||||||
// building the descriptor to pass to ChoosePixelFormat
|
|
||||||
let descriptor = winapi::PIXELFORMATDESCRIPTOR {
|
|
||||||
nSize: mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() as u16,
|
|
||||||
nVersion: 1,
|
|
||||||
dwFlags: winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_SUPPORT_OPENGL | winapi::PFD_DOUBLEBUFFER,
|
|
||||||
iPixelType: winapi::PFD_TYPE_RGBA,
|
|
||||||
cColorBits: 24,
|
|
||||||
cRedBits: 0,
|
|
||||||
cRedShift: 0,
|
|
||||||
cGreenBits: 0,
|
|
||||||
cGreenShift: 0,
|
|
||||||
cBlueBits: 0,
|
|
||||||
cBlueShift: 0,
|
|
||||||
cAlphaBits: 8,
|
|
||||||
cAlphaShift: 0,
|
|
||||||
cAccumBits: 0,
|
|
||||||
cAccumRedBits: 0,
|
|
||||||
cAccumGreenBits: 0,
|
|
||||||
cAccumBlueBits: 0,
|
|
||||||
cAccumAlphaBits: 0,
|
|
||||||
cDepthBits: 24,
|
|
||||||
cStencilBits: 8,
|
|
||||||
cAuxBuffers: 0,
|
|
||||||
iLayerType: winapi::PFD_MAIN_PLANE,
|
|
||||||
bReserved: 0,
|
|
||||||
dwLayerMask: 0,
|
|
||||||
dwVisibleMask: 0,
|
|
||||||
dwDamageMask: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// now querying
|
|
||||||
let pf_id = unsafe { gdi32::ChoosePixelFormat(hdc, &descriptor) };
|
|
||||||
if pf_id == 0 {
|
|
||||||
return Err(CreationError::OsError("No available pixel format".to_owned()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(pf_id)
|
|
||||||
}
|
|
|
@ -9,15 +9,10 @@ use super::WindowState;
|
||||||
use super::Window;
|
use super::Window;
|
||||||
use super::MonitorId;
|
use super::MonitorId;
|
||||||
use super::WindowWrapper;
|
use super::WindowWrapper;
|
||||||
use super::Context;
|
|
||||||
|
|
||||||
use Api;
|
|
||||||
use CreationError;
|
use CreationError;
|
||||||
use CreationError::OsError;
|
use CreationError::OsError;
|
||||||
use CursorState;
|
use CursorState;
|
||||||
use GlAttributes;
|
|
||||||
use GlRequest;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use WindowAttributes;
|
use WindowAttributes;
|
||||||
|
|
||||||
use std::ffi::{OsStr};
|
use std::ffi::{OsStr};
|
||||||
|
@ -29,28 +24,8 @@ use kernel32;
|
||||||
use dwmapi;
|
use dwmapi;
|
||||||
use user32;
|
use user32;
|
||||||
|
|
||||||
use api::wgl::Context as WglContext;
|
pub fn new_window(window: &WindowAttributes) -> Result<Window, CreationError> {
|
||||||
use api::egl;
|
|
||||||
use api::egl::Context as EglContext;
|
|
||||||
use api::egl::ffi::egl::Egl;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum RawContext {
|
|
||||||
Egl(egl::ffi::egl::types::EGLContext),
|
|
||||||
Wgl(winapi::HGLRC),
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for RawContext {}
|
|
||||||
unsafe impl Sync for RawContext {}
|
|
||||||
|
|
||||||
pub fn new_window(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
|
||||||
opengl: &GlAttributes<RawContext>, egl: Option<&Egl>)
|
|
||||||
-> Result<Window, CreationError>
|
|
||||||
{
|
|
||||||
let egl = egl.map(|e| e.clone());
|
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
let pf_reqs = pf_reqs.clone();
|
|
||||||
let opengl = opengl.clone();
|
|
||||||
|
|
||||||
// initializing variables to be sent to the task
|
// initializing variables to be sent to the task
|
||||||
|
|
||||||
|
@ -64,7 +39,7 @@ pub fn new_window(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
unsafe {
|
unsafe {
|
||||||
// creating and sending the `Window`
|
// creating and sending the `Window`
|
||||||
match init(title, &window, &pf_reqs, &opengl, egl) {
|
match init(title, &window) {
|
||||||
Ok(w) => tx.send(Ok(w)).ok(),
|
Ok(w) => tx.send(Ok(w)).ok(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tx.send(Err(e)).ok();
|
tx.send(Err(e)).ok();
|
||||||
|
@ -90,17 +65,7 @@ pub fn new_window(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
||||||
rx.recv().unwrap()
|
rx.recv().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn init(title: Vec<u16>, window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
unsafe fn init(title: Vec<u16>, window: &WindowAttributes) -> Result<Window, CreationError> {
|
||||||
opengl: &GlAttributes<RawContext>, egl: Option<Egl>)
|
|
||||||
-> Result<Window, CreationError>
|
|
||||||
{
|
|
||||||
let opengl = opengl.clone().map_sharing(|sharelists| {
|
|
||||||
match sharelists {
|
|
||||||
RawContext::Wgl(c) => c,
|
|
||||||
_ => unimplemented!()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// registering the window class
|
// registering the window class
|
||||||
let class_name = register_window_class();
|
let class_name = register_window_class();
|
||||||
|
|
||||||
|
@ -172,32 +137,6 @@ unsafe fn init(title: Vec<u16>, window: &WindowAttributes, pf_reqs: &PixelFormat
|
||||||
WindowWrapper(handle, hdc)
|
WindowWrapper(handle, hdc)
|
||||||
};
|
};
|
||||||
|
|
||||||
// creating the OpenGL context
|
|
||||||
let context = match opengl.version {
|
|
||||||
GlRequest::Specific(Api::OpenGlEs, (_major, _minor)) => {
|
|
||||||
if let Some(egl) = egl {
|
|
||||||
if let Ok(c) = EglContext::new(egl, &pf_reqs, &opengl.clone().map_sharing(|_| unimplemented!()),
|
|
||||||
egl::NativeDisplay::Other(Some(ptr::null())))
|
|
||||||
.and_then(|p| p.finish(real_window.0))
|
|
||||||
{
|
|
||||||
Context::Egl(c)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
try!(WglContext::new(&pf_reqs, &opengl, real_window.0)
|
|
||||||
.map(Context::Wgl))
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// falling back to WGL, which is always available
|
|
||||||
try!(WglContext::new(&pf_reqs, &opengl, real_window.0)
|
|
||||||
.map(Context::Wgl))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
try!(WglContext::new(&pf_reqs, &opengl, real_window.0).map(Context::Wgl))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// making the window transparent
|
// making the window transparent
|
||||||
if window.transparent {
|
if window.transparent {
|
||||||
let bb = winapi::DWM_BLURBEHIND {
|
let bb = winapi::DWM_BLURBEHIND {
|
||||||
|
@ -240,7 +179,6 @@ unsafe fn init(title: Vec<u16>, window: &WindowAttributes, pf_reqs: &PixelFormat
|
||||||
// building the struct
|
// building the struct
|
||||||
Ok(Window {
|
Ok(Window {
|
||||||
window: real_window,
|
window: real_window,
|
||||||
context: context,
|
|
||||||
events_receiver: events_receiver,
|
events_receiver: events_receiver,
|
||||||
window_state: window_state,
|
window_state: window_state,
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,15 +10,9 @@ use std::sync::{
|
||||||
};
|
};
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use libc;
|
use libc;
|
||||||
use ContextError;
|
|
||||||
use {CreationError, Event, MouseCursor};
|
use {CreationError, Event, MouseCursor};
|
||||||
use CursorState;
|
use CursorState;
|
||||||
use GlAttributes;
|
|
||||||
use GlContext;
|
|
||||||
|
|
||||||
use Api;
|
|
||||||
use PixelFormat;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use WindowAttributes;
|
use WindowAttributes;
|
||||||
|
|
||||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||||
|
@ -27,12 +21,6 @@ use winapi;
|
||||||
use user32;
|
use user32;
|
||||||
use kernel32;
|
use kernel32;
|
||||||
|
|
||||||
use api::wgl::Context as WglContext;
|
|
||||||
use api::egl::Context as EglContext;
|
|
||||||
use api::egl::ffi::egl::Egl;
|
|
||||||
|
|
||||||
use self::init::RawContext;
|
|
||||||
|
|
||||||
mod callback;
|
mod callback;
|
||||||
mod event;
|
mod event;
|
||||||
mod init;
|
mod init;
|
||||||
|
@ -58,9 +46,6 @@ pub struct Window {
|
||||||
/// Main handle for the window.
|
/// Main handle for the window.
|
||||||
window: WindowWrapper,
|
window: WindowWrapper,
|
||||||
|
|
||||||
/// OpenGL context.
|
|
||||||
context: Context,
|
|
||||||
|
|
||||||
/// Receiver for the events dispatched by the window callback.
|
/// Receiver for the events dispatched by the window callback.
|
||||||
events_receiver: Receiver<Event>,
|
events_receiver: Receiver<Event>,
|
||||||
|
|
||||||
|
@ -71,11 +56,6 @@ pub struct Window {
|
||||||
unsafe impl Send for Window {}
|
unsafe impl Send for Window {}
|
||||||
unsafe impl Sync for Window {}
|
unsafe impl Sync for Window {}
|
||||||
|
|
||||||
enum Context {
|
|
||||||
Egl(EglContext),
|
|
||||||
Wgl(WglContext),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A simple wrapper that destroys the window when it is destroyed.
|
/// A simple wrapper that destroys the window when it is destroyed.
|
||||||
// FIXME: remove `pub` (https://github.com/rust-lang/rust/issues/23585)
|
// FIXME: remove `pub` (https://github.com/rust-lang/rust/issues/23585)
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -109,18 +89,8 @@ impl WindowProxy {
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
/// See the docs in the crate root file.
|
/// See the docs in the crate root file.
|
||||||
pub fn new(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
pub fn new(window: &WindowAttributes) -> Result<Window, CreationError> {
|
||||||
opengl: &GlAttributes<&Window>, egl: Option<&Egl>)
|
init::new_window(window)
|
||||||
-> Result<Window, CreationError>
|
|
||||||
{
|
|
||||||
let opengl = opengl.clone().map_sharing(|sharing| {
|
|
||||||
match sharing.context {
|
|
||||||
Context::Wgl(ref c) => RawContext::Wgl(c.get_hglrc()),
|
|
||||||
Context::Egl(_) => unimplemented!(), // FIXME:
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
init::new_window(window, pf_reqs, &opengl, egl)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
/// See the docs in the crate root file.
|
||||||
|
@ -369,56 +339,6 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlContext for Window {
|
|
||||||
#[inline]
|
|
||||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
match self.context {
|
|
||||||
Context::Wgl(ref c) => c.make_current(),
|
|
||||||
Context::Egl(ref c) => c.make_current(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_current(&self) -> bool {
|
|
||||||
match self.context {
|
|
||||||
Context::Wgl(ref c) => c.is_current(),
|
|
||||||
Context::Egl(ref c) => c.is_current(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
match self.context {
|
|
||||||
Context::Wgl(ref c) => c.get_proc_address(addr),
|
|
||||||
Context::Egl(ref c) => c.get_proc_address(addr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
match self.context {
|
|
||||||
Context::Wgl(ref c) => c.swap_buffers(),
|
|
||||||
Context::Egl(ref c) => c.swap_buffers(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_api(&self) -> Api {
|
|
||||||
match self.context {
|
|
||||||
Context::Wgl(ref c) => c.get_api(),
|
|
||||||
Context::Egl(ref c) => c.get_api(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
match self.context {
|
|
||||||
Context::Wgl(ref c) => c.get_pixel_format(),
|
|
||||||
Context::Egl(ref c) => c.get_pixel_format(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PollEventsIterator<'a> {
|
pub struct PollEventsIterator<'a> {
|
||||||
window: &'a Window,
|
window: &'a Window,
|
||||||
}
|
}
|
||||||
|
|
156
src/headless.rs
156
src/headless.rs
|
@ -1,156 +0,0 @@
|
||||||
use Api;
|
|
||||||
use ContextError;
|
|
||||||
use CreationError;
|
|
||||||
use GlAttributes;
|
|
||||||
use GlRequest;
|
|
||||||
use GlContext;
|
|
||||||
use PixelFormat;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use Robustness;
|
|
||||||
|
|
||||||
use platform;
|
|
||||||
|
|
||||||
/// Object that allows you to build headless contexts.
|
|
||||||
pub struct HeadlessRendererBuilder<'a> {
|
|
||||||
/// The dimensions to use.
|
|
||||||
pub dimensions: (u32, u32),
|
|
||||||
|
|
||||||
/// The OpenGL attributes to build the context with.
|
|
||||||
pub opengl: GlAttributes<&'a platform::HeadlessContext>,
|
|
||||||
|
|
||||||
// Should be made public once it's stabilized.
|
|
||||||
pf_reqs: PixelFormatRequirements,
|
|
||||||
|
|
||||||
/// Platform-specific configuration.
|
|
||||||
platform_specific: platform::PlatformSpecificHeadlessBuilderAttributes,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> HeadlessRendererBuilder<'a> {
|
|
||||||
/// Initializes a new `HeadlessRendererBuilder` with default values.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(width: u32, height: u32) -> HeadlessRendererBuilder<'a> {
|
|
||||||
HeadlessRendererBuilder {
|
|
||||||
dimensions: (width, height),
|
|
||||||
pf_reqs: Default::default(),
|
|
||||||
opengl: Default::default(),
|
|
||||||
platform_specific: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets how the backend should choose the OpenGL API and version.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_gl(mut self, request: GlRequest) -> HeadlessRendererBuilder<'a> {
|
|
||||||
self.opengl.version = request;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the *debug* flag for the OpenGL context.
|
|
||||||
///
|
|
||||||
/// The default value for this flag is `cfg!(ndebug)`, which means that it's enabled
|
|
||||||
/// when you run `cargo build` and disabled when you run `cargo build --release`.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_gl_debug_flag(mut self, flag: bool) -> HeadlessRendererBuilder<'a> {
|
|
||||||
self.opengl.debug = flag;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the robustness of the OpenGL context. See the docs of `Robustness`.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_gl_robustness(mut self, robustness: Robustness) -> HeadlessRendererBuilder<'a> {
|
|
||||||
self.opengl.robustness = robustness;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds the headless context.
|
|
||||||
///
|
|
||||||
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
|
||||||
/// out of memory, etc.
|
|
||||||
#[inline]
|
|
||||||
pub fn build(self) -> Result<HeadlessContext, CreationError> {
|
|
||||||
platform::HeadlessContext::new(self.dimensions, &self.pf_reqs, &self.opengl,
|
|
||||||
&self.platform_specific)
|
|
||||||
.map(|w| HeadlessContext { context: w })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds the headless context.
|
|
||||||
///
|
|
||||||
/// The context is build in a *strict* way. That means that if the backend couldn't give
|
|
||||||
/// you what you requested, an `Err` will be returned.
|
|
||||||
#[inline]
|
|
||||||
pub fn build_strict(self) -> Result<HeadlessContext, CreationError> {
|
|
||||||
self.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a headless OpenGL context.
|
|
||||||
pub struct HeadlessContext {
|
|
||||||
context: platform::HeadlessContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HeadlessContext {
|
|
||||||
/// Creates a new OpenGL context
|
|
||||||
/// Sets the context as the current context.
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
self.context.make_current()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if this context is the current one in this thread.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_current(&self) -> bool {
|
|
||||||
self.context.is_current()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the address of an OpenGL function.
|
|
||||||
///
|
|
||||||
/// Contrary to `wglGetProcAddress`, all available OpenGL functions return an address.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
self.context.get_proc_address(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the API that is currently provided by this window.
|
|
||||||
///
|
|
||||||
/// See `Window::get_api` for more infos.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_api(&self) -> Api {
|
|
||||||
self.context.get_api()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlContext for HeadlessContext {
|
|
||||||
#[inline]
|
|
||||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
self.context.make_current()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_current(&self) -> bool {
|
|
||||||
self.context.is_current()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
self.context.get_proc_address(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
self.context.swap_buffers()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_api(&self) -> Api {
|
|
||||||
self.context.get_api()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
self.context.get_pixel_format()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
332
src/lib.rs
332
src/lib.rs
|
@ -61,7 +61,6 @@ extern crate x11_dl;
|
||||||
extern crate wayland_client;
|
extern crate wayland_client;
|
||||||
|
|
||||||
pub use events::*;
|
pub use events::*;
|
||||||
pub use headless::{HeadlessRendererBuilder, HeadlessContext};
|
|
||||||
pub use window::{WindowBuilder, WindowProxy, PollEventsIterator, WaitEventsIterator};
|
pub use window::{WindowBuilder, WindowProxy, PollEventsIterator, WaitEventsIterator};
|
||||||
pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor};
|
pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor};
|
||||||
pub use native_monitor::NativeMonitorId;
|
pub use native_monitor::NativeMonitorId;
|
||||||
|
@ -73,7 +72,6 @@ use std::cmp::Ordering;
|
||||||
mod api;
|
mod api;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod events;
|
mod events;
|
||||||
mod headless;
|
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
pub mod os;
|
pub mod os;
|
||||||
|
@ -105,44 +103,12 @@ pub struct Window {
|
||||||
window: platform::Window,
|
window: platform::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) -> Result<(), ContextError>;
|
|
||||||
|
|
||||||
/// 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 ();
|
|
||||||
|
|
||||||
/// 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) -> Result<(), ContextError>;
|
|
||||||
|
|
||||||
/// 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.
|
/// Error that can happen while creating a window or a headless renderer.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CreationError {
|
pub enum CreationError {
|
||||||
OsError(String),
|
OsError(String),
|
||||||
/// TODO: remove this error
|
/// TODO: remove this error
|
||||||
NotSupported,
|
NotSupported,
|
||||||
NoBackendAvailable(Box<std::error::Error + Send>),
|
|
||||||
RobustnessNotSupported,
|
|
||||||
OpenGlVersionNotSupported,
|
|
||||||
NoAvailablePixelFormat,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreationError {
|
impl CreationError {
|
||||||
|
@ -150,13 +116,6 @@ impl CreationError {
|
||||||
match *self {
|
match *self {
|
||||||
CreationError::OsError(ref text) => &text,
|
CreationError::OsError(ref text) => &text,
|
||||||
CreationError::NotSupported => "Some of the requested attributes are not supported",
|
CreationError::NotSupported => "Some of the requested attributes are not supported",
|
||||||
CreationError::NoBackendAvailable(_) => "No backend is available",
|
|
||||||
CreationError::RobustnessNotSupported => "You requested robustness, but it is \
|
|
||||||
not supported.",
|
|
||||||
CreationError::OpenGlVersionNotSupported => "The requested OpenGL version is not \
|
|
||||||
supported.",
|
|
||||||
CreationError::NoAvailablePixelFormat => "Couldn't find any pixel format that matches \
|
|
||||||
the criterias.",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,146 +130,6 @@ impl std::error::Error for CreationError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
self.to_string()
|
self.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cause(&self) -> Option<&std::error::Error> {
|
|
||||||
match *self {
|
|
||||||
CreationError::NoBackendAvailable(ref err) => Some(&**err),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error that can happen when manipulating an OpenGL context.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ContextError {
|
|
||||||
IoError(io::Error),
|
|
||||||
ContextLost,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContextError {
|
|
||||||
fn to_string(&self) -> &str {
|
|
||||||
use std::error::Error;
|
|
||||||
match *self {
|
|
||||||
ContextError::IoError(ref err) => err.description(),
|
|
||||||
ContextError::ContextLost => "Context lost"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for ContextError {
|
|
||||||
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
|
||||||
formatter.write_str(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for ContextError {
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
self.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All APIs related to OpenGL that you can possibly get while using glutin.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Api {
|
|
||||||
/// The classical OpenGL. Available on Windows, Linux, OS/X.
|
|
||||||
OpenGl,
|
|
||||||
/// OpenGL embedded system. Available on Linux, Android.
|
|
||||||
OpenGlEs,
|
|
||||||
/// OpenGL for the web. Very similar to OpenGL ES.
|
|
||||||
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 {
|
|
||||||
/// Request the latest version of the "best" API of this platform.
|
|
||||||
///
|
|
||||||
/// On desktop, will try OpenGL.
|
|
||||||
Latest,
|
|
||||||
|
|
||||||
/// Request a specific version of a specific API.
|
|
||||||
///
|
|
||||||
/// Example: `GlRequest::Specific(Api::OpenGl, (3, 3))`.
|
|
||||||
Specific(Api, (u8, u8)),
|
|
||||||
|
|
||||||
/// If OpenGL is available, create an OpenGL context with the specified `opengl_version`.
|
|
||||||
/// Else if OpenGL ES or WebGL is available, create a context with the
|
|
||||||
/// specified `opengles_version`.
|
|
||||||
GlThenGles {
|
|
||||||
/// The version to use for OpenGL.
|
|
||||||
opengl_version: (u8, u8),
|
|
||||||
/// The version to use for OpenGL ES.
|
|
||||||
opengles_version: (u8, u8),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
/// Specifies the tolerance of the OpenGL context to faults. If you accept raw OpenGL commands
|
|
||||||
/// and/or raw shader code from an untrusted source, you should definitely care about this.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum Robustness {
|
|
||||||
/// Not everything is checked. Your application can crash if you do something wrong with your
|
|
||||||
/// shaders.
|
|
||||||
NotRobust,
|
|
||||||
|
|
||||||
/// The driver doesn't check anything. This option is very dangerous. Please know what you're
|
|
||||||
/// doing before using it. See the `GL_KHR_no_error` extension.
|
|
||||||
///
|
|
||||||
/// Since this option is purely an optimisation, no error will be returned if the backend
|
|
||||||
/// doesn't support it. Instead it will automatically fall back to `NotRobust`.
|
|
||||||
NoError,
|
|
||||||
|
|
||||||
/// Everything is checked to avoid any crash. The driver will attempt to avoid any problem,
|
|
||||||
/// but if a problem occurs the behavior is implementation-defined. You are just guaranteed not
|
|
||||||
/// to get a crash.
|
|
||||||
RobustNoResetNotification,
|
|
||||||
|
|
||||||
/// Same as `RobustNoResetNotification` but the context creation doesn't fail if it's not
|
|
||||||
/// supported.
|
|
||||||
TryRobustNoResetNotification,
|
|
||||||
|
|
||||||
/// Everything is checked to avoid any crash. If a problem occurs, the context will enter a
|
|
||||||
/// "context lost" state. It must then be recreated. For the moment, glutin doesn't provide a
|
|
||||||
/// way to recreate a context with the same window :-/
|
|
||||||
RobustLoseContextOnReset,
|
|
||||||
|
|
||||||
/// Same as `RobustLoseContextOnReset` but the context creation doesn't fail if it's not
|
|
||||||
/// supported.
|
|
||||||
TryRobustLoseContextOnReset,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The behavior of the driver when you change the current context.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum ReleaseBehavior {
|
|
||||||
/// Doesn't do anything. Most notably doesn't flush.
|
|
||||||
None,
|
|
||||||
|
|
||||||
/// Flushes the context that was previously current as if `glFlush` was called.
|
|
||||||
Flush,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -386,90 +205,6 @@ pub enum CursorState {
|
||||||
Grab,
|
Grab,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes a possible format. Unused.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PixelFormat {
|
|
||||||
pub hardware_accelerated: bool,
|
|
||||||
pub color_bits: u8,
|
|
||||||
pub alpha_bits: u8,
|
|
||||||
pub depth_bits: u8,
|
|
||||||
pub stencil_bits: u8,
|
|
||||||
pub stereoscopy: bool,
|
|
||||||
pub double_buffer: bool,
|
|
||||||
pub multisampling: Option<u16>,
|
|
||||||
pub srgb: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes how the backend should choose a pixel format.
|
|
||||||
// TODO: swap method? (swap, copy)
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PixelFormatRequirements {
|
|
||||||
/// If true, only hardware-accelerated formats will be conisdered. If false, only software
|
|
||||||
/// renderers. `None` means "don't care". Default is `Some(true)`.
|
|
||||||
pub hardware_accelerated: Option<bool>,
|
|
||||||
|
|
||||||
/// Minimum number of bits for the color buffer, excluding alpha. `None` means "don't care".
|
|
||||||
/// The default is `Some(24)`.
|
|
||||||
pub color_bits: Option<u8>,
|
|
||||||
|
|
||||||
/// If true, the color buffer must be in a floating point format. Default is `false`.
|
|
||||||
///
|
|
||||||
/// Using floating points allows you to write values outside of the `[0.0, 1.0]` range.
|
|
||||||
pub float_color_buffer: bool,
|
|
||||||
|
|
||||||
/// Minimum number of bits for the alpha in the color buffer. `None` means "don't care".
|
|
||||||
/// The default is `Some(8)`.
|
|
||||||
pub alpha_bits: Option<u8>,
|
|
||||||
|
|
||||||
/// Minimum number of bits for the depth buffer. `None` means "don't care".
|
|
||||||
/// The default value is `Some(24)`.
|
|
||||||
pub depth_bits: Option<u8>,
|
|
||||||
|
|
||||||
/// Minimum number of bits for the depth buffer. `None` means "don't care".
|
|
||||||
/// The default value is `Some(8)`.
|
|
||||||
pub stencil_bits: Option<u8>,
|
|
||||||
|
|
||||||
/// If true, only double-buffered formats will be considered. If false, only single-buffer
|
|
||||||
/// formats. `None` means "don't care". The default is `Some(true)`.
|
|
||||||
pub double_buffer: Option<bool>,
|
|
||||||
|
|
||||||
/// Contains the minimum number of samples per pixel in the color, depth and stencil buffers.
|
|
||||||
/// `None` means "don't care". Default is `None`.
|
|
||||||
/// A value of `Some(0)` indicates that multisampling must not be enabled.
|
|
||||||
pub multisampling: Option<u16>,
|
|
||||||
|
|
||||||
/// If true, only stereoscopic formats will be considered. If false, only non-stereoscopic
|
|
||||||
/// formats. The default is `false`.
|
|
||||||
pub stereoscopy: bool,
|
|
||||||
|
|
||||||
/// If true, only sRGB-capable formats will be considered. If false, don't care.
|
|
||||||
/// The default is `false`.
|
|
||||||
pub srgb: bool,
|
|
||||||
|
|
||||||
/// The behavior when changing the current context. Default is `Flush`.
|
|
||||||
pub release_behavior: ReleaseBehavior,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PixelFormatRequirements {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> PixelFormatRequirements {
|
|
||||||
PixelFormatRequirements {
|
|
||||||
hardware_accelerated: Some(true),
|
|
||||||
color_bits: Some(24),
|
|
||||||
float_color_buffer: false,
|
|
||||||
alpha_bits: Some(8),
|
|
||||||
depth_bits: Some(24),
|
|
||||||
stencil_bits: Some(8),
|
|
||||||
double_buffer: None,
|
|
||||||
multisampling: None,
|
|
||||||
stereoscopy: false,
|
|
||||||
srgb: false,
|
|
||||||
release_behavior: ReleaseBehavior::Flush,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attributes to use when creating a window.
|
/// Attributes to use when creating a window.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WindowAttributes {
|
pub struct WindowAttributes {
|
||||||
|
@ -537,73 +272,6 @@ impl Default for WindowAttributes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attributes to use when creating an OpenGL context.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GlAttributes<S> {
|
|
||||||
/// An existing context to share the new the context with.
|
|
||||||
///
|
|
||||||
/// The default is `None`.
|
|
||||||
pub sharing: Option<S>,
|
|
||||||
|
|
||||||
/// Version to try create. See `GlRequest` for more infos.
|
|
||||||
///
|
|
||||||
/// The default is `Latest`.
|
|
||||||
pub version: GlRequest,
|
|
||||||
|
|
||||||
/// OpenGL profile to use.
|
|
||||||
///
|
|
||||||
/// The default is `None`.
|
|
||||||
pub profile: Option<GlProfile>,
|
|
||||||
|
|
||||||
/// Whether to enable the `debug` flag of the context.
|
|
||||||
///
|
|
||||||
/// Debug contexts are usually slower but give better error reporting.
|
|
||||||
///
|
|
||||||
/// The default is `true` in debug mode and `false` in release mode.
|
|
||||||
pub debug: bool,
|
|
||||||
|
|
||||||
/// How the OpenGL context should detect errors.
|
|
||||||
///
|
|
||||||
/// The default is `NotRobust` because this is what is typically expected when you create an
|
|
||||||
/// OpenGL context. However for safety you should consider `TryRobustLoseContextOnReset`.
|
|
||||||
pub robustness: Robustness,
|
|
||||||
|
|
||||||
/// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will block until the
|
|
||||||
/// screen refreshes. This is typically used to prevent screen tearing.
|
|
||||||
///
|
|
||||||
/// The default is `false`.
|
|
||||||
pub vsync: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> GlAttributes<S> {
|
|
||||||
/// Turns the `sharing` parameter into another type by calling a closure.
|
|
||||||
#[inline]
|
|
||||||
pub fn map_sharing<F, T>(self, f: F) -> GlAttributes<T> where F: FnOnce(S) -> T {
|
|
||||||
GlAttributes {
|
|
||||||
sharing: self.sharing.map(f),
|
|
||||||
version: self.version,
|
|
||||||
profile: self.profile,
|
|
||||||
debug: self.debug,
|
|
||||||
robustness: self.robustness,
|
|
||||||
vsync: self.vsync,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Default for GlAttributes<S> {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> GlAttributes<S> {
|
|
||||||
GlAttributes {
|
|
||||||
sharing: None,
|
|
||||||
version: GlRequest::Latest,
|
|
||||||
profile: None,
|
|
||||||
debug: cfg!(debug_assertions),
|
|
||||||
robustness: Robustness::NotRobust,
|
|
||||||
vsync: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod native_monitor {
|
mod native_monitor {
|
||||||
/// Native platform identifier for a monitor. Different platforms use fundamentally different types
|
/// Native platform identifier for a monitor. Different platforms use fundamentally different types
|
||||||
/// to represent a monitor ID.
|
/// to represent a monitor ID.
|
||||||
|
|
|
@ -26,5 +26,5 @@ pub trait WindowBuilderExt {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WindowBuilderExt for WindowBuilder<'a> {
|
impl WindowBuilderExt for WindowBuilder {
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,53 +4,13 @@ pub use api::win32;
|
||||||
pub use api::win32::{MonitorId, get_available_monitors, get_primary_monitor};
|
pub use api::win32::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||||
pub use api::win32::{WindowProxy, PollEventsIterator, WaitEventsIterator};
|
pub use api::win32::{WindowProxy, PollEventsIterator, WaitEventsIterator};
|
||||||
|
|
||||||
use Api;
|
|
||||||
use ContextError;
|
|
||||||
use CreationError;
|
use CreationError;
|
||||||
use PixelFormat;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use GlAttributes;
|
|
||||||
use GlContext;
|
|
||||||
use WindowAttributes;
|
use WindowAttributes;
|
||||||
|
|
||||||
use api::egl::ffi::egl::Egl;
|
|
||||||
use api::egl;
|
|
||||||
use api::egl::Context as EglContext;
|
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use kernel32;
|
use kernel32;
|
||||||
|
|
||||||
/// Stupid wrapper because `*const libc::c_void` doesn't implement `Sync`.
|
|
||||||
struct EglWrapper(Egl);
|
|
||||||
unsafe impl Sync for EglWrapper {}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
// An EGL implementation available on the system.
|
|
||||||
static ref EGL: Option<EglWrapper> = {
|
|
||||||
// the ATI drivers provide an EGL implementation in their DLLs
|
|
||||||
let dll_name = if cfg!(target_pointer_width = "64") {
|
|
||||||
b"atio6axx.dll\0"
|
|
||||||
} else {
|
|
||||||
b"atioglxx.dll\0"
|
|
||||||
};
|
|
||||||
|
|
||||||
let dll = unsafe { kernel32::LoadLibraryA(dll_name.as_ptr() as *const _) };
|
|
||||||
|
|
||||||
if !dll.is_null() {
|
|
||||||
let egl = Egl::load_with(|name| {
|
|
||||||
let name = CString::new(name).unwrap();
|
|
||||||
unsafe { kernel32::GetProcAddress(dll, name.as_ptr()) as *const _ }
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(EglWrapper(egl))
|
|
||||||
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -62,12 +22,10 @@ pub struct Window(win32::Window);
|
||||||
impl Window {
|
impl Window {
|
||||||
/// See the docs in the crate root file.
|
/// See the docs in the crate root file.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
pub fn new(window: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
|
||||||
opengl: &GlAttributes<&Window>, _: &PlatformSpecificWindowBuilderAttributes)
|
|
||||||
-> Result<Window, CreationError>
|
-> Result<Window, CreationError>
|
||||||
{
|
{
|
||||||
win32::Window::new(window, pf_reqs, &opengl.clone().map_sharing(|w| &w.0),
|
win32::Window::new(window).map(Window)
|
||||||
EGL.as_ref().map(|w| &w.0)).map(|w| Window(w))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,87 +44,3 @@ impl DerefMut for Window {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
pub enum HeadlessContext {
|
|
||||||
/// A regular window, but invisible.
|
|
||||||
HiddenWindow(win32::Window),
|
|
||||||
/// An EGL pbuffer.
|
|
||||||
EglPbuffer(EglContext),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HeadlessContext {
|
|
||||||
pub fn new(dimensions: (u32, u32), pf_reqs: &PixelFormatRequirements,
|
|
||||||
opengl: &GlAttributes<&HeadlessContext>,
|
|
||||||
_: &PlatformSpecificHeadlessBuilderAttributes)
|
|
||||||
-> Result<HeadlessContext, CreationError>
|
|
||||||
{
|
|
||||||
// if EGL is available, we try using EGL first
|
|
||||||
// if EGL returns an error, we try the hidden window method
|
|
||||||
if let &Some(ref egl) = &*EGL {
|
|
||||||
let context = EglContext::new(egl.0.clone(), pf_reqs, &opengl.clone().map_sharing(|_| unimplemented!()), // TODO:
|
|
||||||
egl::NativeDisplay::Other(None))
|
|
||||||
.and_then(|prototype| prototype.finish_pbuffer(dimensions))
|
|
||||||
.map(|ctxt| HeadlessContext::EglPbuffer(ctxt));
|
|
||||||
|
|
||||||
if let Ok(context) = context {
|
|
||||||
return Ok(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let window = try!(win32::Window::new(&WindowAttributes { visible: false, .. Default::default() },
|
|
||||||
pf_reqs, &opengl.clone().map_sharing(|_| unimplemented!()), //TODO:
|
|
||||||
EGL.as_ref().map(|w| &w.0)));
|
|
||||||
Ok(HeadlessContext::HiddenWindow(window))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlContext for HeadlessContext {
|
|
||||||
#[inline]
|
|
||||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
match self {
|
|
||||||
&HeadlessContext::HiddenWindow(ref ctxt) => ctxt.make_current(),
|
|
||||||
&HeadlessContext::EglPbuffer(ref ctxt) => ctxt.make_current(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_current(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
&HeadlessContext::HiddenWindow(ref ctxt) => ctxt.is_current(),
|
|
||||||
&HeadlessContext::EglPbuffer(ref ctxt) => ctxt.is_current(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
match self {
|
|
||||||
&HeadlessContext::HiddenWindow(ref ctxt) => ctxt.get_proc_address(addr),
|
|
||||||
&HeadlessContext::EglPbuffer(ref ctxt) => ctxt.get_proc_address(addr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
match self {
|
|
||||||
&HeadlessContext::HiddenWindow(ref ctxt) => ctxt.swap_buffers(),
|
|
||||||
&HeadlessContext::EglPbuffer(ref ctxt) => ctxt.swap_buffers(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_api(&self) -> Api {
|
|
||||||
match self {
|
|
||||||
&HeadlessContext::HiddenWindow(ref ctxt) => ctxt.get_api(),
|
|
||||||
&HeadlessContext::EglPbuffer(ref ctxt) => ctxt.get_api(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
match self {
|
|
||||||
&HeadlessContext::HiddenWindow(ref ctxt) => ctxt.get_pixel_format(),
|
|
||||||
&HeadlessContext::EglPbuffer(ref ctxt) => ctxt.get_pixel_format(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
220
src/window.rs
220
src/window.rs
|
@ -1,19 +1,10 @@
|
||||||
use std::collections::vec_deque::IntoIter as VecDequeIter;
|
use std::collections::vec_deque::IntoIter as VecDequeIter;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
|
||||||
use Api;
|
|
||||||
use ContextError;
|
|
||||||
use CreationError;
|
use CreationError;
|
||||||
use CursorState;
|
use CursorState;
|
||||||
use Event;
|
use Event;
|
||||||
use GlAttributes;
|
|
||||||
use GlContext;
|
|
||||||
use GlProfile;
|
|
||||||
use GlRequest;
|
|
||||||
use MouseCursor;
|
use MouseCursor;
|
||||||
use PixelFormat;
|
|
||||||
use PixelFormatRequirements;
|
|
||||||
use Robustness;
|
|
||||||
use Window;
|
use Window;
|
||||||
use WindowAttributes;
|
use WindowAttributes;
|
||||||
use native_monitor::NativeMonitorId;
|
use native_monitor::NativeMonitorId;
|
||||||
|
@ -22,28 +13,20 @@ use libc;
|
||||||
use platform;
|
use platform;
|
||||||
|
|
||||||
/// Object that allows you to build windows.
|
/// Object that allows you to build windows.
|
||||||
pub struct WindowBuilder<'a> {
|
pub struct WindowBuilder {
|
||||||
/// The attributes to use to create the window.
|
/// The attributes to use to create the window.
|
||||||
pub window: WindowAttributes,
|
pub window: WindowAttributes,
|
||||||
|
|
||||||
/// The attributes to use to create the context.
|
|
||||||
pub opengl: GlAttributes<&'a platform::Window>,
|
|
||||||
|
|
||||||
// Should be made public once it's stabilized.
|
|
||||||
pf_reqs: PixelFormatRequirements,
|
|
||||||
|
|
||||||
/// Platform-specific configuration.
|
/// Platform-specific configuration.
|
||||||
platform_specific: platform::PlatformSpecificWindowBuilderAttributes,
|
platform_specific: platform::PlatformSpecificWindowBuilderAttributes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WindowBuilder<'a> {
|
impl WindowBuilder {
|
||||||
/// Initializes a new `WindowBuilder` with default values.
|
/// Initializes a new `WindowBuilder` with default values.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> WindowBuilder<'a> {
|
pub fn new() -> WindowBuilder {
|
||||||
WindowBuilder {
|
WindowBuilder {
|
||||||
pf_reqs: Default::default(),
|
|
||||||
window: Default::default(),
|
window: Default::default(),
|
||||||
opengl: Default::default(),
|
|
||||||
platform_specific: Default::default(),
|
platform_specific: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +35,7 @@ impl<'a> WindowBuilder<'a> {
|
||||||
///
|
///
|
||||||
/// Width and height are in pixels.
|
/// Width and height are in pixels.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_dimensions(mut self, width: u32, height: u32) -> WindowBuilder<'a> {
|
pub fn with_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
||||||
self.window.dimensions = Some((width, height));
|
self.window.dimensions = Some((width, height));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -61,7 +44,7 @@ impl<'a> WindowBuilder<'a> {
|
||||||
///
|
///
|
||||||
/// Width and height are in pixels.
|
/// Width and height are in pixels.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_min_dimensions(mut self, width: u32, height: u32) -> WindowBuilder<'a> {
|
pub fn with_min_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
||||||
self.window.min_dimensions = Some((width, height));
|
self.window.min_dimensions = Some((width, height));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -70,14 +53,14 @@ impl<'a> WindowBuilder<'a> {
|
||||||
///
|
///
|
||||||
/// Width and height are in pixels.
|
/// Width and height are in pixels.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_max_dimensions(mut self, width: u32, height: u32) -> WindowBuilder<'a> {
|
pub fn with_max_dimensions(mut self, width: u32, height: u32) -> WindowBuilder {
|
||||||
self.window.max_dimensions = Some((width, height));
|
self.window.max_dimensions = Some((width, height));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests a specific title for the window.
|
/// Requests a specific title for the window.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_title(mut self, title: String) -> WindowBuilder<'a> {
|
pub fn with_title(mut self, title: String) -> WindowBuilder {
|
||||||
self.window.title = title;
|
self.window.title = title;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -86,131 +69,36 @@ impl<'a> WindowBuilder<'a> {
|
||||||
///
|
///
|
||||||
/// If you don't specify dimensions for the window, it will match the monitor's.
|
/// If you don't specify dimensions for the window, it will match the monitor's.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_fullscreen(mut self, monitor: MonitorId) -> WindowBuilder<'a> {
|
pub fn with_fullscreen(mut self, monitor: MonitorId) -> WindowBuilder {
|
||||||
let MonitorId(monitor) = monitor;
|
let MonitorId(monitor) = monitor;
|
||||||
self.window.monitor = Some(monitor);
|
self.window.monitor = Some(monitor);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The created window will share all its OpenGL objects with the window in the parameter.
|
|
||||||
///
|
|
||||||
/// There are some exceptions, like FBOs or VAOs. See the OpenGL documentation.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_shared_lists(mut self, other: &'a Window) -> WindowBuilder<'a> {
|
|
||||||
self.opengl.sharing = Some(&other.window);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets how the backend should choose the OpenGL API and version.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_gl(mut self, request: GlRequest) -> WindowBuilder<'a> {
|
|
||||||
self.opengl.version = request;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the desired OpenGL context profile.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_gl_profile(mut self, profile: GlProfile) -> WindowBuilder<'a> {
|
|
||||||
self.opengl.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
|
|
||||||
/// when you run `cargo build` and disabled when you run `cargo build --release`.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_gl_debug_flag(mut self, flag: bool) -> WindowBuilder<'a> {
|
|
||||||
self.opengl.debug = flag;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the robustness of the OpenGL context. See the docs of `Robustness`.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_gl_robustness(mut self, robustness: Robustness) -> WindowBuilder<'a> {
|
|
||||||
self.opengl.robustness = robustness;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Requests that the window has vsync enabled.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_vsync(mut self) -> WindowBuilder<'a> {
|
|
||||||
self.opengl.vsync = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether the window will be initially hidden or visible.
|
/// Sets whether the window will be initially hidden or visible.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_visibility(mut self, visible: bool) -> WindowBuilder<'a> {
|
pub fn with_visibility(mut self, visible: bool) -> WindowBuilder {
|
||||||
self.window.visible = visible;
|
self.window.visible = visible;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the multisampling level to request.
|
|
||||||
///
|
|
||||||
/// # Panic
|
|
||||||
///
|
|
||||||
/// Will panic if `samples` is not a power of two.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_multisampling(mut self, samples: u16) -> WindowBuilder<'a> {
|
|
||||||
assert!(samples.is_power_of_two());
|
|
||||||
self.pf_reqs.multisampling = Some(samples);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the number of bits in the depth buffer.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_depth_buffer(mut self, bits: u8) -> WindowBuilder<'a> {
|
|
||||||
self.pf_reqs.depth_bits = Some(bits);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the number of bits in the stencil buffer.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_stencil_buffer(mut self, bits: u8) -> WindowBuilder<'a> {
|
|
||||||
self.pf_reqs.stencil_bits = Some(bits);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the number of bits in the color buffer.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_pixel_format(mut self, color_bits: u8, alpha_bits: u8) -> WindowBuilder<'a> {
|
|
||||||
self.pf_reqs.color_bits = Some(color_bits);
|
|
||||||
self.pf_reqs.alpha_bits = Some(alpha_bits);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request the backend to be stereoscopic.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_stereoscopy(mut self) -> WindowBuilder<'a> {
|
|
||||||
self.pf_reqs.stereoscopy = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether sRGB should be enabled on the window. `None` means "I don't care".
|
|
||||||
#[inline]
|
|
||||||
pub fn with_srgb(mut self, srgb_enabled: Option<bool>) -> WindowBuilder<'a> {
|
|
||||||
self.pf_reqs.srgb = srgb_enabled.unwrap_or(false);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether the background of the window should be transparent.
|
/// Sets whether the background of the window should be transparent.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_transparency(mut self, transparent: bool) -> WindowBuilder<'a> {
|
pub fn with_transparency(mut self, transparent: bool) -> WindowBuilder {
|
||||||
self.window.transparent = transparent;
|
self.window.transparent = transparent;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets whether the window should have a border, a title bar, etc.
|
/// Sets whether the window should have a border, a title bar, etc.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_decorations(mut self, decorations: bool) -> WindowBuilder<'a> {
|
pub fn with_decorations(mut self, decorations: bool) -> WindowBuilder {
|
||||||
self.window.decorations = decorations;
|
self.window.decorations = decorations;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables multitouch
|
/// Enables multitouch
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_multitouch(mut self) -> WindowBuilder<'a> {
|
pub fn with_multitouch(mut self) -> WindowBuilder {
|
||||||
self.window.multitouch = true;
|
self.window.multitouch = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -231,8 +119,7 @@ impl<'a> WindowBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// building
|
// building
|
||||||
platform::Window::new(&self.window, &self.pf_reqs, &self.opengl, &self.platform_specific)
|
platform::Window::new(&self.window, &self.platform_specific).map(|w| Window { window: w })
|
||||||
.map(|w| Window { window: w })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the window.
|
/// Builds the window.
|
||||||
|
@ -401,39 +288,6 @@ impl Window {
|
||||||
WaitEventsIterator(self.window.wait_events())
|
WaitEventsIterator(self.window.wait_events())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the context as the current context.
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
self.window.make_current()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if this context is the current one in this thread.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_current(&self) -> bool {
|
|
||||||
self.window.is_current()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the address of an OpenGL function.
|
|
||||||
///
|
|
||||||
/// Contrary to `wglGetProcAddress`, all available OpenGL functions return an address.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
self.window.get_proc_address(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
#[inline]
|
|
||||||
pub fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
self.window.swap_buffers()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DEPRECATED. Gets the native platform specific display for this window.
|
/// DEPRECATED. Gets the native platform specific display for this window.
|
||||||
/// This is typically only required when integrating with
|
/// This is typically only required when integrating with
|
||||||
/// other libraries that need this information.
|
/// other libraries that need this information.
|
||||||
|
@ -450,22 +304,6 @@ impl Window {
|
||||||
self.window.platform_window()
|
self.window.platform_window()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the API that is currently provided by this window.
|
|
||||||
///
|
|
||||||
/// - On Windows and OS/X, this always returns `OpenGl`.
|
|
||||||
/// - On Android, this always returns `OpenGlEs`.
|
|
||||||
/// - On Linux, it must be checked at runtime.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_api(&self) -> Api {
|
|
||||||
self.window.get_api()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the pixel format of this window.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
self.window.get_pixel_format()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a window proxy for this window, that can be freely
|
/// Create a window proxy for this window, that can be freely
|
||||||
/// passed to different threads.
|
/// passed to different threads.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -512,38 +350,6 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlContext for Window {
|
|
||||||
#[inline]
|
|
||||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
|
||||||
self.make_current()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_current(&self) -> bool {
|
|
||||||
self.is_current()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
|
||||||
self.get_proc_address(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
|
||||||
self.swap_buffers()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_api(&self) -> Api {
|
|
||||||
self.get_api()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_pixel_format(&self) -> PixelFormat {
|
|
||||||
self.get_pixel_format()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a thread safe subset of operations that can be called
|
/// Represents a thread safe subset of operations that can be called
|
||||||
/// on a window. This structure can be safely cloned and sent between
|
/// on a window. This structure can be safely cloned and sent between
|
||||||
/// threads.
|
/// threads.
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
extern crate glutin;
|
|
||||||
extern crate libc;
|
|
||||||
use glutin::*;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
mod gl {
|
|
||||||
pub use self::Gles2 as Gl;
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/test_gl_bindings.rs"));
|
|
||||||
}
|
|
||||||
use gl::types::*;
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[test]
|
|
||||||
fn test_headless() {
|
|
||||||
let width: i32 = 256;
|
|
||||||
let height: i32 = 256;
|
|
||||||
let window = glutin::HeadlessRendererBuilder::new(width as u32, height as u32).build().unwrap();
|
|
||||||
|
|
||||||
unsafe { window.make_current() };
|
|
||||||
|
|
||||||
let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut framebuffer = 0;
|
|
||||||
let mut texture = 0;
|
|
||||||
gl.GenFramebuffers(1, &mut framebuffer);
|
|
||||||
gl.BindFramebuffer(gl::FRAMEBUFFER, framebuffer);
|
|
||||||
gl.GenTextures(1, &mut texture);
|
|
||||||
gl.BindTexture(gl::TEXTURE_2D, texture);
|
|
||||||
gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
|
|
||||||
gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
|
|
||||||
gl.TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as i32, width, height,
|
|
||||||
0, gl::RGBA, gl::UNSIGNED_BYTE, ptr::null());
|
|
||||||
gl.FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, texture, 0);
|
|
||||||
let status = gl.CheckFramebufferStatus(gl::FRAMEBUFFER);
|
|
||||||
if status != gl::FRAMEBUFFER_COMPLETE {
|
|
||||||
panic!("Error while creating the framebuffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
gl.ClearColor(0.0, 1.0, 0.0, 1.0);
|
|
||||||
gl.Clear(gl::COLOR_BUFFER_BIT);
|
|
||||||
gl.Enable(gl::SCISSOR_TEST);
|
|
||||||
gl.Scissor(1, 0, 1, 1);
|
|
||||||
gl.ClearColor(1.0, 0.0, 0.0, 1.0);
|
|
||||||
gl.Clear(gl::COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
let mut values: Vec<u8> = vec![0;(width*height*4) as usize];
|
|
||||||
gl.ReadPixels(0, 0, width, height, gl::RGBA, gl::UNSIGNED_BYTE, values.as_mut_ptr() as *mut GLvoid);
|
|
||||||
|
|
||||||
assert_eq!(values[0], 0);
|
|
||||||
assert_eq!(values[1], 255);
|
|
||||||
assert_eq!(values[2], 0);
|
|
||||||
assert_eq!(values[3], 255);
|
|
||||||
|
|
||||||
assert_eq!(values[4], 255);
|
|
||||||
assert_eq!(values[5], 0);
|
|
||||||
assert_eq!(values[6], 0);
|
|
||||||
assert_eq!(values[7], 255);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue