Merge pull request #1 from tomaka/cleanup

Remove most OpenGL stuff and make it compile on win32
This commit is contained in:
tomaka 2016-02-23 13:01:04 +01:00
commit 3548276f8f
20 changed files with 30 additions and 3616 deletions

View file

@ -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
View file

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

View file

@ -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";

View file

@ -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,
_ => () _ => ()
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -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 {}

View file

@ -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 {}

View file

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

View file

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

View file

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

View file

@ -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,
} }

View file

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

View file

@ -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.

View file

@ -26,5 +26,5 @@ pub trait WindowBuilderExt {
} }
impl<'a> WindowBuilderExt for WindowBuilder<'a> { impl WindowBuilderExt for WindowBuilder {
} }

View file

@ -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(),
}
}
}

View file

@ -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.

View file

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