Remove most OpenGL stuff and make it compile on win32
This commit is contained in:
parent
bd605478d1
commit
10bb03c5f0
20 changed files with 30 additions and 3616 deletions
|
|
@ -1,37 +0,0 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
|
||||
use libc;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate winapi;
|
||||
|
||||
pub mod egl {
|
||||
pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t;
|
||||
pub type khronos_uint64_t = super::khronos_uint64_t;
|
||||
pub type khronos_ssize_t = super::khronos_ssize_t;
|
||||
pub type EGLNativeDisplayType = super::EGLNativeDisplayType;
|
||||
pub type EGLNativePixmapType = super::EGLNativePixmapType;
|
||||
pub type EGLNativeWindowType = super::EGLNativeWindowType;
|
||||
pub type EGLint = super::EGLint;
|
||||
pub type NativeDisplayType = super::EGLNativeDisplayType;
|
||||
pub type NativePixmapType = super::EGLNativePixmapType;
|
||||
pub type NativeWindowType = super::EGLNativeWindowType;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
|
||||
}
|
||||
|
||||
pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
|
||||
pub type khronos_uint64_t = libc::uint64_t;
|
||||
pub type khronos_ssize_t = libc::c_long;
|
||||
pub type EGLint = libc::int32_t;
|
||||
pub type EGLNativeDisplayType = *const libc::c_void;
|
||||
pub type EGLNativePixmapType = *const libc::c_void; // FIXME: egl_native_pixmap_t instead
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub type EGLNativeWindowType = winapi::HWND;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub type EGLNativeWindowType = *const libc::c_void;
|
||||
#[cfg(target_os = "android")]
|
||||
pub type EGLNativeWindowType = *const libc::c_void;
|
||||
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
|
||||
pub type EGLNativeWindowType = *const libc::c_void;
|
||||
|
|
@ -1,731 +0,0 @@
|
|||
#![cfg(any(target_os = "windows", target_os = "linux", target_os = "android",
|
||||
target_os = "dragonfly", target_os = "freebsd"))]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use ContextError;
|
||||
use CreationError;
|
||||
use GlAttributes;
|
||||
use GlContext;
|
||||
use GlRequest;
|
||||
use PixelFormat;
|
||||
use PixelFormatRequirements;
|
||||
use ReleaseBehavior;
|
||||
use Robustness;
|
||||
use Api;
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::{c_void, c_int};
|
||||
use std::{mem, ptr};
|
||||
|
||||
pub mod ffi;
|
||||
|
||||
/// Specifies the type of display passed as `native_display`.
|
||||
pub enum NativeDisplay {
|
||||
/// `None` means `EGL_DEFAULT_DISPLAY`.
|
||||
X11(Option<ffi::EGLNativeDisplayType>),
|
||||
/// `None` means `EGL_DEFAULT_DISPLAY`.
|
||||
Gbm(Option<ffi::EGLNativeDisplayType>),
|
||||
/// `None` means `EGL_DEFAULT_DISPLAY`.
|
||||
Wayland(Option<ffi::EGLNativeDisplayType>),
|
||||
/// `EGL_DEFAULT_DISPLAY` is mandatory for Android.
|
||||
Android,
|
||||
// TODO: should be `EGLDeviceEXT`
|
||||
Device(ffi::EGLNativeDisplayType),
|
||||
/// Don't specify any display type. Useful on windows. `None` means `EGL_DEFAULT_DISPLAY`.
|
||||
Other(Option<ffi::EGLNativeDisplayType>),
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
egl: ffi::egl::Egl,
|
||||
display: ffi::egl::types::EGLDisplay,
|
||||
context: ffi::egl::types::EGLContext,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
api: Api,
|
||||
pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[inline]
|
||||
fn get_native_display(egl: &ffi::egl::Egl,
|
||||
native_display: NativeDisplay) -> *const c_void {
|
||||
unsafe { egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) }
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
fn get_native_display(egl: &ffi::egl::Egl,
|
||||
native_display: NativeDisplay) -> *const c_void {
|
||||
// the first step is to query the list of extensions without any display, if supported
|
||||
let dp_extensions = unsafe {
|
||||
let p = egl.QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32);
|
||||
|
||||
// this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise
|
||||
// `eglQueryString` returns an error
|
||||
if p.is_null() {
|
||||
vec![]
|
||||
} else {
|
||||
let p = CStr::from_ptr(p);
|
||||
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!(""));
|
||||
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||
}
|
||||
};
|
||||
|
||||
let has_dp_extension = |e: &str| dp_extensions.iter().find(|s| s == &e).is_some();
|
||||
|
||||
match native_display {
|
||||
// Note: Some EGL implementations are missing the `eglGetPlatformDisplay(EXT)` symbol
|
||||
// despite reporting `EGL_EXT_platform_base`. I'm pretty sure this is a bug.
|
||||
// Therefore we detect whether the symbol is loaded in addition to checking for
|
||||
// extensions.
|
||||
NativeDisplay::X11(display) if has_dp_extension("EGL_KHR_platform_x11") &&
|
||||
egl.GetPlatformDisplay.is_loaded() =>
|
||||
{
|
||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
||||
// TODO: `PLATFORM_X11_SCREEN_KHR`
|
||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, d as *mut _,
|
||||
ptr::null()) }
|
||||
},
|
||||
|
||||
NativeDisplay::X11(display) if has_dp_extension("EGL_EXT_platform_x11") &&
|
||||
egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||
{
|
||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
||||
// TODO: `PLATFORM_X11_SCREEN_EXT`
|
||||
unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, d as *mut _,
|
||||
ptr::null()) }
|
||||
},
|
||||
|
||||
NativeDisplay::Gbm(display) if has_dp_extension("EGL_KHR_platform_gbm") &&
|
||||
egl.GetPlatformDisplay.is_loaded() =>
|
||||
{
|
||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, d as *mut _,
|
||||
ptr::null()) }
|
||||
},
|
||||
|
||||
NativeDisplay::Gbm(display) if has_dp_extension("EGL_MESA_platform_gbm") &&
|
||||
egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||
{
|
||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
||||
unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, d as *mut _,
|
||||
ptr::null()) }
|
||||
},
|
||||
|
||||
NativeDisplay::Wayland(display) if has_dp_extension("EGL_KHR_platform_wayland") &&
|
||||
egl.GetPlatformDisplay.is_loaded() =>
|
||||
{
|
||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, d as *mut _,
|
||||
ptr::null()) }
|
||||
},
|
||||
|
||||
NativeDisplay::Wayland(display) if has_dp_extension("EGL_EXT_platform_wayland") &&
|
||||
egl.GetPlatformDisplayEXT.is_loaded() =>
|
||||
{
|
||||
let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
|
||||
unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_WAYLAND_EXT, d as *mut _,
|
||||
ptr::null()) }
|
||||
},
|
||||
|
||||
// TODO: This will never be reached right now, as the android egl bindings
|
||||
// use the static generator, so can't rely on GetPlatformDisplay(EXT).
|
||||
NativeDisplay::Android if has_dp_extension("EGL_KHR_platform_android") &&
|
||||
egl.GetPlatformDisplay.is_loaded() =>
|
||||
{
|
||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_ANDROID_KHR,
|
||||
ffi::egl::DEFAULT_DISPLAY as *mut _, ptr::null()) }
|
||||
},
|
||||
|
||||
NativeDisplay::Device(display) if has_dp_extension("EGL_EXT_platform_device") &&
|
||||
egl.GetPlatformDisplay.is_loaded() =>
|
||||
{
|
||||
unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_DEVICE_EXT, display as *mut _,
|
||||
ptr::null()) }
|
||||
},
|
||||
|
||||
NativeDisplay::X11(Some(display)) | NativeDisplay::Gbm(Some(display)) |
|
||||
NativeDisplay::Wayland(Some(display)) | NativeDisplay::Device(display) |
|
||||
NativeDisplay::Other(Some(display)) => {
|
||||
unsafe { egl.GetDisplay(display as *mut _) }
|
||||
}
|
||||
|
||||
NativeDisplay::X11(None) | NativeDisplay::Gbm(None) | NativeDisplay::Wayland(None) |
|
||||
NativeDisplay::Android | NativeDisplay::Other(None) => {
|
||||
unsafe { egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Start building an EGL context.
|
||||
///
|
||||
/// This function initializes some things and chooses the pixel format.
|
||||
///
|
||||
/// To finish the process, you must call `.finish(window)` on the `ContextPrototype`.
|
||||
pub fn new<'a>(egl: ffi::egl::Egl, pf_reqs: &PixelFormatRequirements,
|
||||
opengl: &'a GlAttributes<&'a Context>, native_display: NativeDisplay)
|
||||
-> Result<ContextPrototype<'a>, CreationError>
|
||||
{
|
||||
if opengl.sharing.is_some() {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// calling `eglGetDisplay` or equivalent
|
||||
let display = get_native_display(&egl, native_display);
|
||||
|
||||
if display.is_null() {
|
||||
return Err(CreationError::OsError("Could not create EGL display object".to_string()));
|
||||
}
|
||||
|
||||
let egl_version = unsafe {
|
||||
let mut major: ffi::egl::types::EGLint = mem::uninitialized();
|
||||
let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
|
||||
|
||||
if egl.Initialize(display, &mut major, &mut minor) == 0 {
|
||||
return Err(CreationError::OsError(format!("eglInitialize failed")))
|
||||
}
|
||||
|
||||
(major, minor)
|
||||
};
|
||||
|
||||
// the list of extensions supported by the client once initialized is different from the
|
||||
// list of extensions obtained earlier
|
||||
let extensions = if egl_version >= (1, 2) {
|
||||
let p = unsafe { CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)) };
|
||||
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!(""));
|
||||
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// binding the right API and choosing the version
|
||||
let (version, api) = unsafe {
|
||||
match opengl.version {
|
||||
GlRequest::Latest => {
|
||||
if egl_version >= (1, 4) {
|
||||
if egl.BindAPI(ffi::egl::OPENGL_API) != 0 {
|
||||
(None, Api::OpenGl)
|
||||
} else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 {
|
||||
(None, Api::OpenGlEs)
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
} else {
|
||||
(None, Api::OpenGlEs)
|
||||
}
|
||||
},
|
||||
GlRequest::Specific(Api::OpenGlEs, version) => {
|
||||
if egl_version >= (1, 2) {
|
||||
if egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
}
|
||||
(Some(version), Api::OpenGlEs)
|
||||
},
|
||||
GlRequest::Specific(Api::OpenGl, version) => {
|
||||
if egl_version < (1, 4) {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
if egl.BindAPI(ffi::egl::OPENGL_API) == 0 {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
(Some(version), Api::OpenGl)
|
||||
},
|
||||
GlRequest::Specific(_, _) => return Err(CreationError::OpenGlVersionNotSupported),
|
||||
GlRequest::GlThenGles { opengles_version, opengl_version } => {
|
||||
if egl_version >= (1, 4) {
|
||||
if egl.BindAPI(ffi::egl::OPENGL_API) != 0 {
|
||||
(Some(opengl_version), Api::OpenGl)
|
||||
} else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 {
|
||||
(Some(opengles_version), Api::OpenGlEs)
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
} else {
|
||||
(Some(opengles_version), Api::OpenGlEs)
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let (config_id, pixel_format) = unsafe {
|
||||
try!(choose_fbconfig(&egl, display, &egl_version, api, version, pf_reqs))
|
||||
};
|
||||
|
||||
Ok(ContextPrototype {
|
||||
opengl: opengl,
|
||||
egl: egl,
|
||||
display: display,
|
||||
egl_version: egl_version,
|
||||
extensions: extensions,
|
||||
api: api,
|
||||
version: version,
|
||||
config_id: config_id,
|
||||
pixel_format: pixel_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GlContext for Context {
|
||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
||||
let ret = self.egl.MakeCurrent(self.display, self.surface, self.surface, self.context);
|
||||
|
||||
if ret == 0 {
|
||||
match self.egl.GetError() as u32 {
|
||||
ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost),
|
||||
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_current(&self) -> bool {
|
||||
unsafe { self.egl.GetCurrentContext() == self.context }
|
||||
}
|
||||
|
||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
unsafe {
|
||||
self.egl.GetProcAddress(addr) as *const _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
||||
let ret = unsafe {
|
||||
self.egl.SwapBuffers(self.display, self.surface)
|
||||
};
|
||||
|
||||
if ret == 0 {
|
||||
match unsafe { self.egl.GetError() } as u32 {
|
||||
ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost),
|
||||
err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_api(&self) -> Api {
|
||||
self.api
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.pixel_format.clone()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Context {}
|
||||
unsafe impl Sync for Context {}
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
||||
// is still the current one
|
||||
self.egl.DestroyContext(self.display, self.context);
|
||||
self.egl.DestroySurface(self.display, self.surface);
|
||||
self.egl.Terminate(self.display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContextPrototype<'a> {
|
||||
opengl: &'a GlAttributes<&'a Context>,
|
||||
egl: ffi::egl::Egl,
|
||||
display: ffi::egl::types::EGLDisplay,
|
||||
egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint),
|
||||
extensions: Vec<String>,
|
||||
api: Api,
|
||||
version: Option<(u8, u8)>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
impl<'a> ContextPrototype<'a> {
|
||||
pub fn get_native_visual_id(&self) -> ffi::egl::types::EGLint {
|
||||
let mut value = unsafe { mem::uninitialized() };
|
||||
let ret = unsafe { self.egl.GetConfigAttrib(self.display, self.config_id,
|
||||
ffi::egl::NATIVE_VISUAL_ID
|
||||
as ffi::egl::types::EGLint, &mut value) };
|
||||
if ret == 0 { panic!("eglGetConfigAttrib failed") };
|
||||
value
|
||||
}
|
||||
|
||||
pub fn finish(self, native_window: ffi::EGLNativeWindowType)
|
||||
-> Result<Context, CreationError>
|
||||
{
|
||||
let surface = unsafe {
|
||||
let surface = self.egl.CreateWindowSurface(self.display, self.config_id, native_window,
|
||||
ptr::null());
|
||||
if surface.is_null() {
|
||||
return Err(CreationError::OsError(format!("eglCreateWindowSurface failed")))
|
||||
}
|
||||
surface
|
||||
};
|
||||
|
||||
self.finish_impl(surface)
|
||||
}
|
||||
|
||||
pub fn finish_pbuffer(self, dimensions: (u32, u32)) -> Result<Context, CreationError> {
|
||||
let attrs = &[
|
||||
ffi::egl::WIDTH as c_int, dimensions.0 as c_int,
|
||||
ffi::egl::HEIGHT as c_int, dimensions.1 as c_int,
|
||||
ffi::egl::NONE as c_int,
|
||||
];
|
||||
|
||||
let surface = unsafe {
|
||||
let surface = self.egl.CreatePbufferSurface(self.display, self.config_id,
|
||||
attrs.as_ptr());
|
||||
if surface.is_null() {
|
||||
return Err(CreationError::OsError(format!("eglCreatePbufferSurface failed")))
|
||||
}
|
||||
surface
|
||||
};
|
||||
|
||||
self.finish_impl(surface)
|
||||
}
|
||||
|
||||
fn finish_impl(self, surface: ffi::egl::types::EGLSurface)
|
||||
-> Result<Context, CreationError>
|
||||
{
|
||||
let context = unsafe {
|
||||
if let Some(version) = self.version {
|
||||
try!(create_context(&self.egl, self.display, &self.egl_version,
|
||||
&self.extensions, self.api, version, self.config_id,
|
||||
self.opengl.debug, self.opengl.robustness))
|
||||
|
||||
} else if self.api == Api::OpenGlEs {
|
||||
if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (2, 0), self.config_id,
|
||||
self.opengl.debug, self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (1, 0),
|
||||
self.config_id, self.opengl.debug,
|
||||
self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
|
||||
} else {
|
||||
if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (3, 2), self.config_id,
|
||||
self.opengl.debug, self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (3, 1),
|
||||
self.config_id, self.opengl.debug,
|
||||
self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version,
|
||||
&self.extensions, self.api, (1, 0),
|
||||
self.config_id, self.opengl.debug,
|
||||
self.opengl.robustness)
|
||||
{
|
||||
ctxt
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Context {
|
||||
egl: self.egl,
|
||||
display: self.display,
|
||||
context: context,
|
||||
surface: surface,
|
||||
api: self.api,
|
||||
pixel_format: self.pixel_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn choose_fbconfig(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay,
|
||||
egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
|
||||
api: Api, version: Option<(u8, u8)>, reqs: &PixelFormatRequirements)
|
||||
-> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError>
|
||||
{
|
||||
let descriptor = {
|
||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
||||
|
||||
if egl_version >= &(1, 2) {
|
||||
out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
|
||||
out.push(ffi::egl::RGB_BUFFER as c_int);
|
||||
}
|
||||
|
||||
out.push(ffi::egl::SURFACE_TYPE as c_int);
|
||||
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
|
||||
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
|
||||
out.push((ffi::egl::WINDOW_BIT) as c_int);
|
||||
|
||||
match (api, version) {
|
||||
(Api::OpenGlEs, Some((3, _))) => {
|
||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
|
||||
out.push(ffi::egl::CONFORMANT as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
|
||||
},
|
||||
(Api::OpenGlEs, Some((2, _))) => {
|
||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
|
||||
out.push(ffi::egl::CONFORMANT as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
|
||||
},
|
||||
(Api::OpenGlEs, Some((1, _))) => {
|
||||
if egl_version >= &(1, 3) {
|
||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES_BIT as c_int);
|
||||
out.push(ffi::egl::CONFORMANT as c_int);
|
||||
out.push(ffi::egl::OPENGL_ES_BIT as c_int);
|
||||
}
|
||||
},
|
||||
(Api::OpenGlEs, _) => unimplemented!(),
|
||||
(Api::OpenGl, _) => {
|
||||
if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); }
|
||||
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
|
||||
out.push(ffi::egl::OPENGL_BIT as c_int);
|
||||
out.push(ffi::egl::CONFORMANT as c_int);
|
||||
out.push(ffi::egl::OPENGL_BIT as c_int);
|
||||
},
|
||||
(_, _) => unimplemented!(),
|
||||
};
|
||||
|
||||
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
|
||||
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
|
||||
out.push(if hardware_accelerated {
|
||||
ffi::egl::NONE as c_int
|
||||
} else {
|
||||
ffi::egl::SLOW_CONFIG as c_int
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(color) = reqs.color_bits {
|
||||
out.push(ffi::egl::RED_SIZE as c_int);
|
||||
out.push((color / 3) as c_int);
|
||||
out.push(ffi::egl::GREEN_SIZE as c_int);
|
||||
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
|
||||
out.push(ffi::egl::BLUE_SIZE as c_int);
|
||||
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
|
||||
}
|
||||
|
||||
if let Some(alpha) = reqs.alpha_bits {
|
||||
out.push(ffi::egl::ALPHA_SIZE as c_int);
|
||||
out.push(alpha as c_int);
|
||||
}
|
||||
|
||||
if let Some(depth) = reqs.depth_bits {
|
||||
out.push(ffi::egl::DEPTH_SIZE as c_int);
|
||||
out.push(depth as c_int);
|
||||
}
|
||||
|
||||
if let Some(stencil) = reqs.stencil_bits {
|
||||
out.push(ffi::egl::STENCIL_SIZE as c_int);
|
||||
out.push(stencil as c_int);
|
||||
}
|
||||
|
||||
if let Some(true) = reqs.double_buffer {
|
||||
return Err(CreationError::NoAvailablePixelFormat);
|
||||
}
|
||||
|
||||
if let Some(multisampling) = reqs.multisampling {
|
||||
out.push(ffi::egl::SAMPLES as c_int);
|
||||
out.push(multisampling as c_int);
|
||||
}
|
||||
|
||||
if reqs.stereoscopy {
|
||||
return Err(CreationError::NoAvailablePixelFormat);
|
||||
}
|
||||
|
||||
// FIXME: srgb is not taken into account
|
||||
|
||||
match reqs.release_behavior {
|
||||
ReleaseBehavior::Flush => (),
|
||||
ReleaseBehavior::None => {
|
||||
// TODO: with EGL you need to manually set the behavior
|
||||
unimplemented!()
|
||||
},
|
||||
}
|
||||
|
||||
out.push(ffi::egl::NONE as c_int);
|
||||
out
|
||||
};
|
||||
|
||||
// calling `eglChooseConfig`
|
||||
let mut config_id = mem::uninitialized();
|
||||
let mut num_configs = mem::uninitialized();
|
||||
if egl.ChooseConfig(display, descriptor.as_ptr(), &mut config_id, 1, &mut num_configs) == 0 {
|
||||
return Err(CreationError::OsError(format!("eglChooseConfig failed")));
|
||||
}
|
||||
if num_configs == 0 {
|
||||
return Err(CreationError::NoAvailablePixelFormat);
|
||||
}
|
||||
|
||||
// analyzing each config
|
||||
macro_rules! attrib {
|
||||
($egl:expr, $display:expr, $config:expr, $attr:expr) => (
|
||||
{
|
||||
let mut value = mem::uninitialized();
|
||||
let res = $egl.GetConfigAttrib($display, $config,
|
||||
$attr as ffi::egl::types::EGLint, &mut value);
|
||||
if res == 0 {
|
||||
return Err(CreationError::OsError(format!("eglGetConfigAttrib failed")));
|
||||
}
|
||||
value
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
let desc = PixelFormat {
|
||||
hardware_accelerated: attrib!(egl, display, config_id, ffi::egl::CONFIG_CAVEAT)
|
||||
!= ffi::egl::SLOW_CONFIG as i32,
|
||||
color_bits: attrib!(egl, display, config_id, ffi::egl::RED_SIZE) as u8 +
|
||||
attrib!(egl, display, config_id, ffi::egl::BLUE_SIZE) as u8 +
|
||||
attrib!(egl, display, config_id, ffi::egl::GREEN_SIZE) as u8,
|
||||
alpha_bits: attrib!(egl, display, config_id, ffi::egl::ALPHA_SIZE) as u8,
|
||||
depth_bits: attrib!(egl, display, config_id, ffi::egl::DEPTH_SIZE) as u8,
|
||||
stencil_bits: attrib!(egl, display, config_id, ffi::egl::STENCIL_SIZE) as u8,
|
||||
stereoscopy: false,
|
||||
double_buffer: true,
|
||||
multisampling: match attrib!(egl, display, config_id, ffi::egl::SAMPLES) {
|
||||
0 | 1 => None,
|
||||
a => Some(a as u16),
|
||||
},
|
||||
srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
|
||||
};
|
||||
|
||||
Ok((config_id, desc))
|
||||
}
|
||||
|
||||
unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay,
|
||||
egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
|
||||
extensions: &[String], api: Api, version: (u8, u8),
|
||||
config_id: ffi::egl::types::EGLConfig, gl_debug: bool,
|
||||
gl_robustness: Robustness)
|
||||
-> Result<ffi::egl::types::EGLContext, CreationError>
|
||||
{
|
||||
let mut context_attributes = Vec::with_capacity(10);
|
||||
let mut flags = 0;
|
||||
|
||||
if egl_version >= &(1, 5) || extensions.iter().find(|s| s == &"EGL_KHR_create_context")
|
||||
.is_some()
|
||||
{
|
||||
context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
|
||||
context_attributes.push(version.0 as i32);
|
||||
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
|
||||
context_attributes.push(version.1 as i32);
|
||||
|
||||
// handling robustness
|
||||
let supports_robustness = egl_version >= &(1, 5) ||
|
||||
extensions.iter()
|
||||
.find(|s| s == &"EGL_EXT_create_context_robustness")
|
||||
.is_some();
|
||||
|
||||
match gl_robustness {
|
||||
Robustness::NotRobust => (),
|
||||
|
||||
Robustness::NoError => {
|
||||
if extensions.iter().find(|s| s == &"EGL_KHR_create_context_no_error").is_some() {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as c_int);
|
||||
context_attributes.push(1);
|
||||
}
|
||||
},
|
||||
|
||||
Robustness::RobustNoResetNotification => {
|
||||
if supports_robustness {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
||||
as c_int);
|
||||
context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as c_int);
|
||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
||||
} else {
|
||||
return Err(CreationError::RobustnessNotSupported);
|
||||
}
|
||||
},
|
||||
|
||||
Robustness::TryRobustNoResetNotification => {
|
||||
if supports_robustness {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
||||
as c_int);
|
||||
context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as c_int);
|
||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
||||
}
|
||||
},
|
||||
|
||||
Robustness::RobustLoseContextOnReset => {
|
||||
if supports_robustness {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
||||
as c_int);
|
||||
context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int);
|
||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
||||
} else {
|
||||
return Err(CreationError::RobustnessNotSupported);
|
||||
}
|
||||
},
|
||||
|
||||
Robustness::TryRobustLoseContextOnReset => {
|
||||
if supports_robustness {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
|
||||
as c_int);
|
||||
context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int);
|
||||
flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if gl_debug {
|
||||
if egl_version >= &(1, 5) {
|
||||
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
|
||||
context_attributes.push(ffi::egl::TRUE as i32);
|
||||
}
|
||||
|
||||
// TODO: using this flag sometimes generates an error
|
||||
// there was a change in the specs that added this flag, so it may not be
|
||||
// supported everywhere ; however it is not possible to know whether it is
|
||||
// supported or not
|
||||
//flags = flags | ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32;
|
||||
}
|
||||
|
||||
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
|
||||
context_attributes.push(flags);
|
||||
|
||||
} else if egl_version >= &(1, 3) && api == Api::OpenGlEs {
|
||||
// robustness is not supported
|
||||
match gl_robustness {
|
||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
||||
return Err(CreationError::RobustnessNotSupported);
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
|
||||
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
|
||||
context_attributes.push(version.0 as i32);
|
||||
}
|
||||
|
||||
context_attributes.push(ffi::egl::NONE as i32);
|
||||
|
||||
let context = egl.CreateContext(display, config_id, ptr::null(),
|
||||
context_attributes.as_ptr());
|
||||
|
||||
if context.is_null() {
|
||||
match egl.GetError() as u32 {
|
||||
ffi::egl::BAD_ATTRIBUTE => return Err(CreationError::OpenGlVersionNotSupported),
|
||||
e => panic!("eglCreateContext failed: 0x{:x}", e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(context)
|
||||
}
|
||||
|
|
@ -1,498 +0,0 @@
|
|||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
|
||||
|
||||
use ContextError;
|
||||
use CreationError;
|
||||
use GlAttributes;
|
||||
use GlContext;
|
||||
use GlProfile;
|
||||
use GlRequest;
|
||||
use Api;
|
||||
use PixelFormat;
|
||||
use PixelFormatRequirements;
|
||||
use ReleaseBehavior;
|
||||
use Robustness;
|
||||
|
||||
use libc;
|
||||
use libc::c_int;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use api::x11::ffi;
|
||||
|
||||
use platform::Window as PlatformWindow;
|
||||
|
||||
pub struct Context {
|
||||
glx: ffi::glx::Glx,
|
||||
display: *mut ffi::Display,
|
||||
window: ffi::Window,
|
||||
context: ffi::GLXContext,
|
||||
pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
// TODO: remove me
|
||||
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
|
||||
use std::ffi::CString;
|
||||
let c_str = CString::new(s.as_bytes().to_vec()).unwrap();
|
||||
f(c_str.as_ptr())
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new<'a>(glx: ffi::glx::Glx, xlib: &ffi::Xlib, pf_reqs: &PixelFormatRequirements,
|
||||
opengl: &'a GlAttributes<&'a Context>, display: *mut ffi::Display,
|
||||
screen_id: libc::c_int) -> Result<ContextPrototype<'a>, CreationError>
|
||||
{
|
||||
// This is completely ridiculous, but VirtualBox's OpenGL driver needs some call handled by
|
||||
// *it* (i.e. not Mesa) to occur before anything else can happen. That is because
|
||||
// VirtualBox's OpenGL driver is going to apply binary patches to Mesa in the DLL
|
||||
// constructor and until it's loaded it won't have a chance to do that.
|
||||
//
|
||||
// The easiest way to do this is to just call `glXQueryVersion()` before doing anything
|
||||
// else. See: https://www.virtualbox.org/ticket/8293
|
||||
let (mut major, mut minor) = (0, 0);
|
||||
unsafe {
|
||||
glx.QueryVersion(display as *mut _, &mut major, &mut minor);
|
||||
}
|
||||
|
||||
// loading the list of extensions
|
||||
let extensions = unsafe {
|
||||
let extensions = glx.QueryExtensionsString(display as *mut _, screen_id);
|
||||
let extensions = CStr::from_ptr(extensions).to_bytes().to_vec();
|
||||
String::from_utf8(extensions).unwrap()
|
||||
};
|
||||
|
||||
// finding the pixel format we want
|
||||
let (fb_config, pixel_format) = unsafe {
|
||||
try!(choose_fbconfig(&glx, &extensions, xlib, display, screen_id, pf_reqs)
|
||||
.map_err(|_| CreationError::NoAvailablePixelFormat))
|
||||
};
|
||||
|
||||
// getting the visual infos
|
||||
let visual_infos: ffi::glx::types::XVisualInfo = unsafe {
|
||||
let vi = glx.GetVisualFromFBConfig(display as *mut _, fb_config);
|
||||
if vi.is_null() {
|
||||
return Err(CreationError::OsError(format!("glxGetVisualFromFBConfig failed")));
|
||||
}
|
||||
let vi_copy = ptr::read(vi as *const _);
|
||||
(xlib.XFree)(vi as *mut _);
|
||||
vi_copy
|
||||
};
|
||||
|
||||
Ok(ContextPrototype {
|
||||
glx: glx,
|
||||
extensions: extensions,
|
||||
opengl: opengl,
|
||||
display: display,
|
||||
fb_config: fb_config,
|
||||
visual_infos: unsafe { mem::transmute(visual_infos) },
|
||||
pixel_format: pixel_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GlContext for Context {
|
||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
||||
// TODO: glutin needs some internal changes for proper error recovery
|
||||
let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context);
|
||||
if res == 0 {
|
||||
panic!("glx::MakeCurrent failed");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_current(&self) -> bool {
|
||||
unsafe { self.glx.GetCurrentContext() == self.context }
|
||||
}
|
||||
|
||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
unsafe {
|
||||
self.glx.GetProcAddress(addr as *const _) as *const _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
||||
// TODO: glutin needs some internal changes for proper error recovery
|
||||
unsafe { self.glx.SwapBuffers(self.display as *mut _, self.window); }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_api(&self) -> ::Api {
|
||||
::Api::OpenGl
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.pixel_format.clone()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Context {}
|
||||
unsafe impl Sync for Context {}
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.is_current() {
|
||||
self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null_mut());
|
||||
}
|
||||
|
||||
self.glx.DestroyContext(self.display as *mut _, self.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContextPrototype<'a> {
|
||||
glx: ffi::glx::Glx,
|
||||
extensions: String,
|
||||
opengl: &'a GlAttributes<&'a Context>,
|
||||
display: *mut ffi::Display,
|
||||
fb_config: ffi::glx::types::GLXFBConfig,
|
||||
visual_infos: ffi::XVisualInfo,
|
||||
pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
impl<'a> ContextPrototype<'a> {
|
||||
#[inline]
|
||||
pub fn get_visual_infos(&self) -> &ffi::XVisualInfo {
|
||||
&self.visual_infos
|
||||
}
|
||||
|
||||
pub fn finish(self, window: ffi::Window) -> Result<Context, CreationError> {
|
||||
let share = match self.opengl.sharing {
|
||||
Some(ctxt) => ctxt.context,
|
||||
None => ptr::null()
|
||||
};
|
||||
|
||||
// loading the extra GLX functions
|
||||
let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {
|
||||
with_c_str(addr, |s| {
|
||||
unsafe { self.glx.GetProcAddress(s as *const u8) as *const _ }
|
||||
})
|
||||
});
|
||||
|
||||
// creating GL context
|
||||
let context = match self.opengl.version {
|
||||
GlRequest::Latest => {
|
||||
if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &self.extensions, (3, 2),
|
||||
self.opengl.profile, self.opengl.debug,
|
||||
self.opengl.robustness, share,
|
||||
self.display, self.fb_config, &self.visual_infos)
|
||||
{
|
||||
ctxt
|
||||
} else if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &self.extensions,
|
||||
(3, 1), self.opengl.profile,
|
||||
self.opengl.debug,
|
||||
self.opengl.robustness, share, self.display,
|
||||
self.fb_config, &self.visual_infos)
|
||||
{
|
||||
ctxt
|
||||
|
||||
} else {
|
||||
try!(create_context(&self.glx, &extra_functions, &self.extensions, (1, 0),
|
||||
self.opengl.profile, self.opengl.debug,
|
||||
self.opengl.robustness,
|
||||
share, self.display, self.fb_config, &self.visual_infos))
|
||||
}
|
||||
},
|
||||
GlRequest::Specific(Api::OpenGl, (major, minor)) => {
|
||||
try!(create_context(&self.glx, &extra_functions, &self.extensions, (major, minor),
|
||||
self.opengl.profile, self.opengl.debug,
|
||||
self.opengl.robustness, share, self.display, self.fb_config,
|
||||
&self.visual_infos))
|
||||
},
|
||||
GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
|
||||
GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
|
||||
try!(create_context(&self.glx, &extra_functions, &self.extensions, (major, minor),
|
||||
self.opengl.profile, self.opengl.debug,
|
||||
self.opengl.robustness, share, self.display, self.fb_config,
|
||||
&self.visual_infos))
|
||||
},
|
||||
};
|
||||
|
||||
// vsync
|
||||
if self.opengl.vsync {
|
||||
unsafe { self.glx.MakeCurrent(self.display as *mut _, window, context) };
|
||||
|
||||
if extra_functions.SwapIntervalEXT.is_loaded() {
|
||||
// this should be the most common extension
|
||||
unsafe {
|
||||
extra_functions.SwapIntervalEXT(self.display as *mut _, window, 1);
|
||||
}
|
||||
|
||||
// checking that it worked
|
||||
// TODO: handle this
|
||||
/*if self.builder.strict {
|
||||
let mut swap = unsafe { mem::uninitialized() };
|
||||
unsafe {
|
||||
self.glx.QueryDrawable(self.display as *mut _, window,
|
||||
ffi::glx_extra::SWAP_INTERVAL_EXT as i32,
|
||||
&mut swap);
|
||||
}
|
||||
|
||||
if swap != 1 {
|
||||
return Err(CreationError::OsError(format!("Couldn't setup vsync: expected \
|
||||
interval `1` but got `{}`", swap)));
|
||||
}
|
||||
}*/
|
||||
|
||||
// GLX_MESA_swap_control is not official
|
||||
/*} else if extra_functions.SwapIntervalMESA.is_loaded() {
|
||||
unsafe {
|
||||
extra_functions.SwapIntervalMESA(1);
|
||||
}*/
|
||||
|
||||
} else if extra_functions.SwapIntervalSGI.is_loaded() {
|
||||
unsafe {
|
||||
extra_functions.SwapIntervalSGI(1);
|
||||
}
|
||||
|
||||
}/* else if self.builder.strict {
|
||||
// TODO: handle this
|
||||
return Err(CreationError::OsError(format!("Couldn't find any available vsync extension")));
|
||||
}*/
|
||||
|
||||
unsafe { self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null()) };
|
||||
}
|
||||
|
||||
Ok(Context {
|
||||
glx: self.glx,
|
||||
display: self.display,
|
||||
window: window,
|
||||
context: context,
|
||||
pixel_format: self.pixel_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str,
|
||||
version: (u8, u8), profile: Option<GlProfile>, debug: bool,
|
||||
robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display,
|
||||
fb_config: ffi::glx::types::GLXFBConfig,
|
||||
visual_infos: &ffi::XVisualInfo)
|
||||
-> Result<ffi::GLXContext, CreationError>
|
||||
{
|
||||
unsafe {
|
||||
let context = if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context").is_some() {
|
||||
let mut attributes = Vec::with_capacity(9);
|
||||
|
||||
attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
|
||||
attributes.push(version.0 as c_int);
|
||||
attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
|
||||
attributes.push(version.1 as c_int);
|
||||
|
||||
if let Some(profile) = profile {
|
||||
let flag = match profile {
|
||||
GlProfile::Compatibility =>
|
||||
ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
GlProfile::Core =>
|
||||
ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
};
|
||||
|
||||
attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
|
||||
attributes.push(flag as c_int);
|
||||
}
|
||||
|
||||
let flags = {
|
||||
let mut flags = 0;
|
||||
|
||||
// robustness
|
||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context_robustness").is_some() {
|
||||
match robustness {
|
||||
Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => {
|
||||
attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
|
||||
attributes.push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as c_int);
|
||||
flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
|
||||
},
|
||||
Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => {
|
||||
attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
|
||||
attributes.push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int);
|
||||
flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
|
||||
},
|
||||
Robustness::NotRobust => (),
|
||||
Robustness::NoError => (),
|
||||
}
|
||||
} else {
|
||||
match robustness {
|
||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
||||
return Err(CreationError::RobustnessNotSupported);
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int;
|
||||
}
|
||||
|
||||
flags
|
||||
};
|
||||
|
||||
attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as c_int);
|
||||
attributes.push(flags);
|
||||
|
||||
attributes.push(0);
|
||||
|
||||
extra_functions.CreateContextAttribsARB(display as *mut _, fb_config, share, 1,
|
||||
attributes.as_ptr())
|
||||
|
||||
} else {
|
||||
let visual_infos: *const ffi::XVisualInfo = visual_infos;
|
||||
glx.CreateContext(display as *mut _, visual_infos as *mut _, share, 1)
|
||||
};
|
||||
|
||||
if context.is_null() {
|
||||
// TODO: check for errors and return `OpenGlVersionNotSupported`
|
||||
return Err(CreationError::OsError(format!("GL context creation failed")));
|
||||
}
|
||||
|
||||
Ok(context)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerates all available FBConfigs
|
||||
unsafe fn choose_fbconfig(glx: &ffi::glx::Glx, extensions: &str, xlib: &ffi::Xlib,
|
||||
display: *mut ffi::Display, screen_id: libc::c_int,
|
||||
reqs: &PixelFormatRequirements)
|
||||
-> Result<(ffi::glx::types::GLXFBConfig, PixelFormat), ()>
|
||||
{
|
||||
let descriptor = {
|
||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
||||
|
||||
out.push(ffi::glx::X_RENDERABLE as c_int);
|
||||
out.push(1);
|
||||
|
||||
out.push(ffi::glx::X_VISUAL_TYPE as c_int);
|
||||
out.push(ffi::glx::TRUE_COLOR as c_int);
|
||||
|
||||
out.push(ffi::glx::DRAWABLE_TYPE as c_int);
|
||||
out.push(ffi::glx::WINDOW_BIT as c_int);
|
||||
|
||||
out.push(ffi::glx::RENDER_TYPE as c_int);
|
||||
if reqs.float_color_buffer {
|
||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_fbconfig_float").is_some() {
|
||||
out.push(ffi::glx_extra::RGBA_FLOAT_BIT_ARB as c_int);
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
out.push(ffi::glx::RGBA_BIT as c_int);
|
||||
}
|
||||
|
||||
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
|
||||
out.push(ffi::glx::CONFIG_CAVEAT as c_int);
|
||||
out.push(if hardware_accelerated {
|
||||
ffi::glx::NONE as c_int
|
||||
} else {
|
||||
ffi::glx::SLOW_CONFIG as c_int
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(color) = reqs.color_bits {
|
||||
out.push(ffi::glx::RED_SIZE as c_int);
|
||||
out.push((color / 3) as c_int);
|
||||
out.push(ffi::glx::GREEN_SIZE as c_int);
|
||||
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
|
||||
out.push(ffi::glx::BLUE_SIZE as c_int);
|
||||
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
|
||||
}
|
||||
|
||||
if let Some(alpha) = reqs.alpha_bits {
|
||||
out.push(ffi::glx::ALPHA_SIZE as c_int);
|
||||
out.push(alpha as c_int);
|
||||
}
|
||||
|
||||
if let Some(depth) = reqs.depth_bits {
|
||||
out.push(ffi::glx::DEPTH_SIZE as c_int);
|
||||
out.push(depth as c_int);
|
||||
}
|
||||
|
||||
if let Some(stencil) = reqs.stencil_bits {
|
||||
out.push(ffi::glx::STENCIL_SIZE as c_int);
|
||||
out.push(stencil as c_int);
|
||||
}
|
||||
|
||||
let double_buffer = reqs.double_buffer.unwrap_or(true);
|
||||
out.push(ffi::glx::DOUBLEBUFFER as c_int);
|
||||
out.push(if double_buffer { 1 } else { 0 });
|
||||
|
||||
if let Some(multisampling) = reqs.multisampling {
|
||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_multisample").is_some() {
|
||||
out.push(ffi::glx_extra::SAMPLE_BUFFERS_ARB as c_int);
|
||||
out.push(if multisampling == 0 { 0 } else { 1 });
|
||||
out.push(ffi::glx_extra::SAMPLES_ARB as c_int);
|
||||
out.push(multisampling as c_int);
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
out.push(ffi::glx::STEREO as c_int);
|
||||
out.push(if reqs.stereoscopy { 1 } else { 0 });
|
||||
|
||||
if reqs.srgb {
|
||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_framebuffer_sRGB").is_some() {
|
||||
out.push(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int);
|
||||
out.push(1);
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
match reqs.release_behavior {
|
||||
ReleaseBehavior::Flush => (),
|
||||
ReleaseBehavior::None => {
|
||||
if extensions.split(' ').find(|&i| i == "GLX_ARB_context_flush_control").is_some() {
|
||||
out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int);
|
||||
out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
out.push(0);
|
||||
out
|
||||
};
|
||||
|
||||
// calling glXChooseFBConfig
|
||||
let fb_config = {
|
||||
let mut num_configs = 1;
|
||||
let result = glx.ChooseFBConfig(display as *mut _, screen_id, descriptor.as_ptr(),
|
||||
&mut num_configs);
|
||||
if result.is_null() { return Err(()); }
|
||||
if num_configs == 0 { return Err(()); }
|
||||
let val = *result;
|
||||
(xlib.XFree)(result as *mut _);
|
||||
val
|
||||
};
|
||||
|
||||
let get_attrib = |attrib: c_int| -> i32 {
|
||||
let mut value = 0;
|
||||
glx.GetFBConfigAttrib(display as *mut _, fb_config, attrib, &mut value);
|
||||
// TODO: check return value
|
||||
value
|
||||
};
|
||||
|
||||
let pf_desc = PixelFormat {
|
||||
hardware_accelerated: get_attrib(ffi::glx::CONFIG_CAVEAT as c_int) !=
|
||||
ffi::glx::SLOW_CONFIG as c_int,
|
||||
color_bits: get_attrib(ffi::glx::RED_SIZE as c_int) as u8 +
|
||||
get_attrib(ffi::glx::GREEN_SIZE as c_int) as u8 +
|
||||
get_attrib(ffi::glx::BLUE_SIZE as c_int) as u8,
|
||||
alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as c_int) as u8,
|
||||
depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as c_int) as u8,
|
||||
stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as c_int) as u8,
|
||||
stereoscopy: get_attrib(ffi::glx::STEREO as c_int) != 0,
|
||||
double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as c_int) != 0,
|
||||
multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as c_int) != 0 {
|
||||
Some(get_attrib(ffi::glx::SAMPLES as c_int) as u16)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0,
|
||||
};
|
||||
|
||||
Ok((fb_config, pf_desc))
|
||||
}
|
||||
|
|
@ -2,12 +2,8 @@ pub mod android;
|
|||
pub mod caca;
|
||||
pub mod cocoa;
|
||||
pub mod dlopen;
|
||||
pub mod egl;
|
||||
pub mod emscripten;
|
||||
pub mod glx;
|
||||
pub mod osmesa;
|
||||
pub mod wayland;
|
||||
pub mod wgl;
|
||||
pub mod win32;
|
||||
pub mod x11;
|
||||
pub mod ios;
|
||||
|
|
|
|||
|
|
@ -1,140 +0,0 @@
|
|||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
|
||||
|
||||
extern crate osmesa_sys;
|
||||
|
||||
use Api;
|
||||
use ContextError;
|
||||
use CreationError;
|
||||
use GlAttributes;
|
||||
use GlContext;
|
||||
use PixelFormat;
|
||||
use PixelFormatRequirements;
|
||||
use Robustness;
|
||||
use libc;
|
||||
use std::{mem, ptr};
|
||||
use std::ffi::CString;
|
||||
|
||||
pub struct OsMesaContext {
|
||||
context: osmesa_sys::OSMesaContext,
|
||||
buffer: Vec<u32>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
pub enum OsMesaCreationError {
|
||||
CreationError(CreationError),
|
||||
NotSupported,
|
||||
}
|
||||
|
||||
impl From<CreationError> for OsMesaCreationError {
|
||||
#[inline]
|
||||
fn from(e: CreationError) -> OsMesaCreationError {
|
||||
OsMesaCreationError::CreationError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl OsMesaContext {
|
||||
pub fn new(dimensions: (u32, u32), pf_reqs: &PixelFormatRequirements,
|
||||
opengl: &GlAttributes<&OsMesaContext>) -> Result<OsMesaContext, OsMesaCreationError>
|
||||
{
|
||||
if let Err(_) = osmesa_sys::OsMesa::try_loading() {
|
||||
return Err(OsMesaCreationError::NotSupported);
|
||||
}
|
||||
|
||||
if opengl.sharing.is_some() { unimplemented!() } // TODO: proper error
|
||||
|
||||
match opengl.robustness {
|
||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
||||
return Err(CreationError::RobustnessNotSupported.into());
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
|
||||
// TODO: use `pf_reqs` for the format
|
||||
// TODO: check OpenGL version and return `OpenGlVersionNotSupported` if necessary
|
||||
|
||||
Ok(OsMesaContext {
|
||||
width: dimensions.0,
|
||||
height: dimensions.1,
|
||||
buffer: ::std::iter::repeat(unsafe { mem::uninitialized() })
|
||||
.take((dimensions.0 * dimensions.1) as usize).collect(),
|
||||
context: unsafe {
|
||||
let ctxt = osmesa_sys::OSMesaCreateContext(0x1908, ptr::null_mut());
|
||||
if ctxt.is_null() {
|
||||
return Err(CreationError::OsError("OSMesaCreateContext failed".to_string()).into());
|
||||
}
|
||||
ctxt
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_framebuffer(&self) -> &[u32] {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
// TODO: can we remove this without causing havoc?
|
||||
#[inline]
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
}
|
||||
|
||||
impl GlContext for OsMesaContext {
|
||||
#[inline]
|
||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
||||
let ret = osmesa_sys::OSMesaMakeCurrent(self.context, self.buffer.as_ptr()
|
||||
as *mut _, 0x1401, self.width
|
||||
as libc::c_int, self.height as libc::c_int);
|
||||
|
||||
// an error can only happen in case of invalid parameter, which would indicate a bug
|
||||
// in glutin
|
||||
if ret == 0 {
|
||||
panic!("OSMesaMakeCurrent failed");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_current(&self) -> bool {
|
||||
unsafe { osmesa_sys::OSMesaGetCurrentContext() == self.context }
|
||||
}
|
||||
|
||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
unsafe {
|
||||
let c_str = CString::new(addr.as_bytes().to_vec()).unwrap();
|
||||
mem::transmute(osmesa_sys::OSMesaGetProcAddress(mem::transmute(c_str.as_ptr())))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_api(&self) -> Api {
|
||||
Api::OpenGl
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_pixel_format(&self) -> PixelFormat {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OsMesaContext {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { osmesa_sys::OSMesaDestroyContext(self.context) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for OsMesaContext {}
|
||||
unsafe impl Sync for OsMesaContext {}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
/// WGL bindings
|
||||
pub mod wgl {
|
||||
include!(concat!(env!("OUT_DIR"), "/wgl_bindings.rs"));
|
||||
}
|
||||
|
||||
/// Functions that are not necessarly always available
|
||||
pub mod wgl_extra {
|
||||
include!(concat!(env!("OUT_DIR"), "/wgl_extra_bindings.rs"));
|
||||
}
|
||||
|
||||
#[link(name = "opengl32")]
|
||||
extern {}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::io;
|
||||
|
||||
use winapi;
|
||||
use CreationError;
|
||||
|
||||
use super::gl;
|
||||
/// A guard for when you want to make the context current. Destroying the guard restores the
|
||||
/// previously-current context.
|
||||
pub struct CurrentContextGuard<'a, 'b> {
|
||||
previous_hdc: winapi::HDC,
|
||||
previous_hglrc: winapi::HGLRC,
|
||||
marker1: PhantomData<&'a ()>,
|
||||
marker2: PhantomData<&'b ()>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> CurrentContextGuard<'a, 'b> {
|
||||
pub unsafe fn make_current(hdc: winapi::HDC, context: winapi::HGLRC)
|
||||
-> Result<CurrentContextGuard<'a, 'b>, CreationError>
|
||||
{
|
||||
let previous_hdc = gl::wgl::GetCurrentDC() as winapi::HDC;
|
||||
let previous_hglrc = gl::wgl::GetCurrentContext() as winapi::HGLRC;
|
||||
|
||||
let result = gl::wgl::MakeCurrent(hdc as *const _, context as *const _);
|
||||
if result == 0 {
|
||||
return Err(CreationError::OsError(format!("wglMakeCurrent function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
Ok(CurrentContextGuard {
|
||||
previous_hdc: previous_hdc,
|
||||
previous_hglrc: previous_hglrc,
|
||||
marker1: PhantomData,
|
||||
marker2: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::wgl::MakeCurrent(self.previous_hdc as *const c_void,
|
||||
self.previous_hglrc as *const c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,787 +0,0 @@
|
|||
#![cfg(any(target_os = "windows"))]
|
||||
|
||||
use ContextError;
|
||||
use CreationError;
|
||||
use GlAttributes;
|
||||
use GlContext;
|
||||
use GlRequest;
|
||||
use GlProfile;
|
||||
use PixelFormat;
|
||||
use PixelFormatRequirements;
|
||||
use ReleaseBehavior;
|
||||
use Robustness;
|
||||
use Api;
|
||||
|
||||
use self::make_current_guard::CurrentContextGuard;
|
||||
|
||||
use std::ffi::{CStr, CString, OsStr};
|
||||
use std::os::raw::{c_void, c_int};
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::{mem, ptr};
|
||||
use std::io;
|
||||
|
||||
use winapi;
|
||||
use kernel32;
|
||||
use user32;
|
||||
use gdi32;
|
||||
|
||||
mod make_current_guard;
|
||||
mod gl;
|
||||
|
||||
/// A WGL context.
|
||||
///
|
||||
/// Note: should be destroyed before its window.
|
||||
pub struct Context {
|
||||
context: ContextWrapper,
|
||||
|
||||
hdc: winapi::HDC,
|
||||
|
||||
/// Binded to `opengl32.dll`.
|
||||
///
|
||||
/// `wglGetProcAddress` returns null for GL 1.1 functions because they are
|
||||
/// already defined by the system. This module contains them.
|
||||
gl_library: winapi::HMODULE,
|
||||
|
||||
/// The pixel format that has been used to create this context.
|
||||
pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
/// A simple wrapper that destroys the window when it is destroyed.
|
||||
struct WindowWrapper(winapi::HWND, winapi::HDC);
|
||||
|
||||
impl Drop for WindowWrapper {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
user32::DestroyWindow(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps around a context so that it is destroyed when necessary.
|
||||
struct ContextWrapper(winapi::HGLRC);
|
||||
|
||||
impl Drop for ContextWrapper {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::wgl::DeleteContext(self.0 as *const _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Attempt to build a new WGL context on a window.
|
||||
///
|
||||
/// The window must **not** have had `SetPixelFormat` called on it.
|
||||
///
|
||||
/// # Unsafety
|
||||
///
|
||||
/// The `window` must continue to exist as long as the resulting `Context` exists.
|
||||
pub unsafe fn new(pf_reqs: &PixelFormatRequirements, opengl: &GlAttributes<winapi::HGLRC>,
|
||||
window: winapi::HWND) -> Result<Context, CreationError>
|
||||
{
|
||||
let hdc = user32::GetDC(window);
|
||||
if hdc.is_null() {
|
||||
let err = Err(CreationError::OsError(format!("GetDC function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
return err;
|
||||
}
|
||||
|
||||
// loading the functions that are not guaranteed to be supported
|
||||
let extra_functions = try!(load_extra_functions(window));
|
||||
|
||||
// getting the list of the supported extensions
|
||||
let extensions = if extra_functions.GetExtensionsStringARB.is_loaded() {
|
||||
let data = extra_functions.GetExtensionsStringARB(hdc as *const _);
|
||||
let data = CStr::from_ptr(data).to_bytes().to_vec();
|
||||
String::from_utf8(data).unwrap()
|
||||
|
||||
} else if extra_functions.GetExtensionsStringEXT.is_loaded() {
|
||||
let data = extra_functions.GetExtensionsStringEXT();
|
||||
let data = CStr::from_ptr(data).to_bytes().to_vec();
|
||||
String::from_utf8(data).unwrap()
|
||||
|
||||
} else {
|
||||
format!("")
|
||||
};
|
||||
|
||||
// calling SetPixelFormat
|
||||
let pixel_format = {
|
||||
let (id, f) = if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format")
|
||||
.is_some()
|
||||
{
|
||||
try!(choose_arb_pixel_format(&extra_functions, &extensions, hdc, pf_reqs)
|
||||
.map_err(|_| CreationError::NoAvailablePixelFormat))
|
||||
} else {
|
||||
try!(choose_native_pixel_format(hdc, pf_reqs)
|
||||
.map_err(|_| CreationError::NoAvailablePixelFormat))
|
||||
};
|
||||
|
||||
try!(set_pixel_format(hdc, id));
|
||||
f
|
||||
};
|
||||
|
||||
// creating the OpenGL context
|
||||
let context = try!(create_context(Some((&extra_functions, pf_reqs, opengl, &extensions)),
|
||||
window, hdc));
|
||||
|
||||
// loading the opengl32 module
|
||||
let gl_library = try!(load_opengl32_dll());
|
||||
|
||||
// handling vsync
|
||||
if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() {
|
||||
let _guard = try!(CurrentContextGuard::make_current(hdc, context.0));
|
||||
|
||||
if extra_functions.SwapIntervalEXT(if opengl.vsync { 1 } else { 0 }) == 0 {
|
||||
return Err(CreationError::OsError(format!("wglSwapIntervalEXT failed")));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Context {
|
||||
context: context,
|
||||
hdc: hdc,
|
||||
gl_library: gl_library,
|
||||
pixel_format: pixel_format,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the raw HGLRC.
|
||||
#[inline]
|
||||
pub fn get_hglrc(&self) -> winapi::HGLRC {
|
||||
self.context.0
|
||||
}
|
||||
}
|
||||
|
||||
impl GlContext for Context {
|
||||
#[inline]
|
||||
unsafe fn make_current(&self) -> Result<(), ContextError> {
|
||||
if gl::wgl::MakeCurrent(self.hdc as *const _, self.context.0 as *const _) != 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ContextError::IoError(io::Error::last_os_error()))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_current(&self) -> bool {
|
||||
unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const c_void }
|
||||
}
|
||||
|
||||
fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
|
||||
unsafe {
|
||||
let p = gl::wgl::GetProcAddress(addr) as *const _;
|
||||
if !p.is_null() { return p; }
|
||||
kernel32::GetProcAddress(self.gl_library, addr) as *const _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn swap_buffers(&self) -> Result<(), ContextError> {
|
||||
// TODO: decide how to handle the error
|
||||
/*if unsafe { gdi32::SwapBuffers(self.hdc) } != 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ContextError::IoError(io::Error::last_os_error()))
|
||||
}*/
|
||||
unsafe { gdi32::SwapBuffers(self.hdc) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_api(&self) -> Api {
|
||||
// FIXME: can be opengl es
|
||||
Api::OpenGl
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.pixel_format.clone()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Context {}
|
||||
unsafe impl Sync for Context {}
|
||||
|
||||
/// Creates an OpenGL context.
|
||||
///
|
||||
/// If `extra` is `Some`, this function will attempt to use the latest WGL functions to create the
|
||||
/// context.
|
||||
///
|
||||
/// Otherwise, only the basic API will be used and the chances of `CreationError::NotSupported`
|
||||
/// being returned increase.
|
||||
unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &PixelFormatRequirements,
|
||||
&GlAttributes<winapi::HGLRC>, &str)>,
|
||||
_: winapi::HWND, hdc: winapi::HDC)
|
||||
-> Result<ContextWrapper, CreationError>
|
||||
{
|
||||
let share;
|
||||
|
||||
if let Some((extra_functions, pf_reqs, opengl, extensions)) = extra {
|
||||
share = opengl.sharing.unwrap_or(ptr::null_mut());
|
||||
|
||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context").is_some() {
|
||||
let mut attributes = Vec::new();
|
||||
|
||||
match opengl.version {
|
||||
GlRequest::Latest => {},
|
||||
GlRequest::Specific(Api::OpenGl, (major, minor)) => {
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
|
||||
attributes.push(major as c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
|
||||
attributes.push(minor as c_int);
|
||||
},
|
||||
GlRequest::Specific(Api::OpenGlEs, (major, minor)) => {
|
||||
if extensions.split(' ').find(|&i| i == "WGL_EXT_create_context_es2_profile")
|
||||
.is_some()
|
||||
{
|
||||
attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT as c_int);
|
||||
} else {
|
||||
return Err(CreationError::OpenGlVersionNotSupported);
|
||||
}
|
||||
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
|
||||
attributes.push(major as c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
|
||||
attributes.push(minor as c_int);
|
||||
},
|
||||
GlRequest::Specific(_, _) => return Err(CreationError::OpenGlVersionNotSupported),
|
||||
GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
|
||||
attributes.push(major as c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
|
||||
attributes.push(minor as c_int);
|
||||
},
|
||||
}
|
||||
|
||||
if let Some(profile) = opengl.profile {
|
||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_profile").is_some()
|
||||
{
|
||||
let flag = match profile {
|
||||
GlProfile::Compatibility =>
|
||||
gl::wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
GlProfile::Core =>
|
||||
gl::wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
};
|
||||
attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
|
||||
attributes.push(flag as c_int);
|
||||
} else {
|
||||
return Err(CreationError::NotSupported);
|
||||
}
|
||||
}
|
||||
|
||||
let flags = {
|
||||
let mut flags = 0;
|
||||
|
||||
// robustness
|
||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_robustness").is_some() {
|
||||
match opengl.robustness {
|
||||
Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => {
|
||||
attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
|
||||
attributes.push(gl::wgl_extra::NO_RESET_NOTIFICATION_ARB as c_int);
|
||||
flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
|
||||
},
|
||||
Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => {
|
||||
attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
|
||||
attributes.push(gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int);
|
||||
flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
|
||||
},
|
||||
Robustness::NotRobust => (),
|
||||
Robustness::NoError => (),
|
||||
}
|
||||
} else {
|
||||
match opengl.robustness {
|
||||
Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
|
||||
return Err(CreationError::RobustnessNotSupported);
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
if opengl.debug {
|
||||
flags = flags | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as c_int;
|
||||
}
|
||||
|
||||
flags
|
||||
};
|
||||
|
||||
attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as c_int);
|
||||
attributes.push(flags);
|
||||
|
||||
attributes.push(0);
|
||||
|
||||
let ctxt = extra_functions.CreateContextAttribsARB(hdc as *const c_void,
|
||||
share as *const c_void,
|
||||
attributes.as_ptr());
|
||||
|
||||
if ctxt.is_null() {
|
||||
return Err(CreationError::OsError(format!("wglCreateContextAttribsARB failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
} else {
|
||||
return Ok(ContextWrapper(ctxt as winapi::HGLRC));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
share = ptr::null_mut();
|
||||
}
|
||||
|
||||
let ctxt = gl::wgl::CreateContext(hdc as *const c_void);
|
||||
if ctxt.is_null() {
|
||||
return Err(CreationError::OsError(format!("wglCreateContext failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
if !share.is_null() {
|
||||
if gl::wgl::ShareLists(share as *const c_void, ctxt) == 0 {
|
||||
return Err(CreationError::OsError(format!("wglShareLists failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ContextWrapper(ctxt as winapi::HGLRC))
|
||||
}
|
||||
|
||||
/// Chooses a pixel formats without using WGL.
|
||||
///
|
||||
/// Gives less precise results than `enumerate_arb_pixel_formats`.
|
||||
unsafe fn choose_native_pixel_format(hdc: winapi::HDC, reqs: &PixelFormatRequirements)
|
||||
-> Result<(c_int, PixelFormat), ()>
|
||||
{
|
||||
// TODO: hardware acceleration is not handled
|
||||
|
||||
// handling non-supported stuff
|
||||
if reqs.float_color_buffer {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
match reqs.multisampling {
|
||||
Some(0) => (),
|
||||
None => (),
|
||||
Some(_) => return Err(())
|
||||
};
|
||||
|
||||
if reqs.stereoscopy {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if reqs.srgb {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if reqs.release_behavior != ReleaseBehavior::Flush {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// building the descriptor to pass to ChoosePixelFormat
|
||||
let descriptor = winapi::PIXELFORMATDESCRIPTOR {
|
||||
nSize: mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() as u16,
|
||||
nVersion: 1,
|
||||
dwFlags: {
|
||||
let f1 = match reqs.double_buffer {
|
||||
None => winapi::PFD_DOUBLEBUFFER, // Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose
|
||||
Some(true) => winapi::PFD_DOUBLEBUFFER,
|
||||
Some(false) => 0,
|
||||
};
|
||||
|
||||
let f2 = if reqs.stereoscopy {
|
||||
winapi::PFD_STEREO
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_SUPPORT_OPENGL | f1 | f2
|
||||
},
|
||||
iPixelType: winapi::PFD_TYPE_RGBA,
|
||||
cColorBits: reqs.color_bits.unwrap_or(0),
|
||||
cRedBits: 0,
|
||||
cRedShift: 0,
|
||||
cGreenBits: 0,
|
||||
cGreenShift: 0,
|
||||
cBlueBits: 0,
|
||||
cBlueShift: 0,
|
||||
cAlphaBits: reqs.alpha_bits.unwrap_or(0),
|
||||
cAlphaShift: 0,
|
||||
cAccumBits: 0,
|
||||
cAccumRedBits: 0,
|
||||
cAccumGreenBits: 0,
|
||||
cAccumBlueBits: 0,
|
||||
cAccumAlphaBits: 0,
|
||||
cDepthBits: reqs.depth_bits.unwrap_or(0),
|
||||
cStencilBits: reqs.stencil_bits.unwrap_or(0),
|
||||
cAuxBuffers: 0,
|
||||
iLayerType: winapi::PFD_MAIN_PLANE,
|
||||
bReserved: 0,
|
||||
dwLayerMask: 0,
|
||||
dwVisibleMask: 0,
|
||||
dwDamageMask: 0,
|
||||
};
|
||||
|
||||
// now querying
|
||||
let pf_id = gdi32::ChoosePixelFormat(hdc, &descriptor);
|
||||
if pf_id == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// querying back the capabilities of what windows told us
|
||||
let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed();
|
||||
if gdi32::DescribePixelFormat(hdc, pf_id, mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() as u32,
|
||||
&mut output) == 0
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// windows may return us a non-conforming pixel format if none are supported, so we have to
|
||||
// check this
|
||||
if (output.dwFlags & winapi::PFD_DRAW_TO_WINDOW) == 0 {
|
||||
return Err(());
|
||||
}
|
||||
if (output.dwFlags & winapi::PFD_SUPPORT_OPENGL) == 0 {
|
||||
return Err(());
|
||||
}
|
||||
if output.iPixelType != winapi::PFD_TYPE_RGBA {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let pf_desc = PixelFormat {
|
||||
hardware_accelerated: (output.dwFlags & winapi::PFD_GENERIC_FORMAT) == 0,
|
||||
color_bits: output.cRedBits + output.cGreenBits + output.cBlueBits,
|
||||
alpha_bits: output.cAlphaBits,
|
||||
depth_bits: output.cDepthBits,
|
||||
stencil_bits: output.cStencilBits,
|
||||
stereoscopy: (output.dwFlags & winapi::PFD_STEREO) != 0,
|
||||
double_buffer: (output.dwFlags & winapi::PFD_DOUBLEBUFFER) != 0,
|
||||
multisampling: None,
|
||||
srgb: false,
|
||||
};
|
||||
|
||||
if pf_desc.alpha_bits < reqs.alpha_bits.unwrap_or(0) {
|
||||
return Err(());
|
||||
}
|
||||
if pf_desc.depth_bits < reqs.depth_bits.unwrap_or(0) {
|
||||
return Err(());
|
||||
}
|
||||
if pf_desc.stencil_bits < reqs.stencil_bits.unwrap_or(0) {
|
||||
return Err(());
|
||||
}
|
||||
if pf_desc.color_bits < reqs.color_bits.unwrap_or(0) {
|
||||
return Err(());
|
||||
}
|
||||
if let Some(req) = reqs.hardware_accelerated {
|
||||
if pf_desc.hardware_accelerated != req {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
if let Some(req) = reqs.double_buffer {
|
||||
if pf_desc.double_buffer != req {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok((pf_id, pf_desc))
|
||||
}
|
||||
|
||||
/// Enumerates the list of pixel formats by using extra WGL functions.
|
||||
///
|
||||
/// Gives more precise results than `enumerate_native_pixel_formats`.
|
||||
unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str,
|
||||
hdc: winapi::HDC, reqs: &PixelFormatRequirements)
|
||||
-> Result<(c_int, PixelFormat), ()>
|
||||
{
|
||||
let descriptor = {
|
||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
||||
|
||||
out.push(gl::wgl_extra::DRAW_TO_WINDOW_ARB as c_int);
|
||||
out.push(1);
|
||||
|
||||
out.push(gl::wgl_extra::SUPPORT_OPENGL_ARB as c_int);
|
||||
out.push(1);
|
||||
|
||||
out.push(gl::wgl_extra::PIXEL_TYPE_ARB as c_int);
|
||||
if reqs.float_color_buffer {
|
||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format_float").is_some() {
|
||||
out.push(gl::wgl_extra::TYPE_RGBA_FLOAT_ARB as c_int);
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
out.push(gl::wgl_extra::TYPE_RGBA_ARB as c_int);
|
||||
}
|
||||
|
||||
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
|
||||
out.push(gl::wgl_extra::ACCELERATION_ARB as c_int);
|
||||
out.push(if hardware_accelerated {
|
||||
gl::wgl_extra::FULL_ACCELERATION_ARB as c_int
|
||||
} else {
|
||||
gl::wgl_extra::NO_ACCELERATION_ARB as c_int
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(color) = reqs.color_bits {
|
||||
out.push(gl::wgl_extra::COLOR_BITS_ARB as c_int);
|
||||
out.push(color as c_int);
|
||||
}
|
||||
|
||||
if let Some(alpha) = reqs.alpha_bits {
|
||||
out.push(gl::wgl_extra::ALPHA_BITS_ARB as c_int);
|
||||
out.push(alpha as c_int);
|
||||
}
|
||||
|
||||
if let Some(depth) = reqs.depth_bits {
|
||||
out.push(gl::wgl_extra::DEPTH_BITS_ARB as c_int);
|
||||
out.push(depth as c_int);
|
||||
}
|
||||
|
||||
if let Some(stencil) = reqs.stencil_bits {
|
||||
out.push(gl::wgl_extra::STENCIL_BITS_ARB as c_int);
|
||||
out.push(stencil as c_int);
|
||||
}
|
||||
|
||||
// Prefer double buffering if unspecified (probably shouldn't once you can choose)
|
||||
let double_buffer = reqs.double_buffer.unwrap_or(true);
|
||||
out.push(gl::wgl_extra::DOUBLE_BUFFER_ARB as c_int);
|
||||
out.push(if double_buffer { 1 } else { 0 });
|
||||
|
||||
if let Some(multisampling) = reqs.multisampling {
|
||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() {
|
||||
out.push(gl::wgl_extra::SAMPLE_BUFFERS_ARB as c_int);
|
||||
out.push(if multisampling == 0 { 0 } else { 1 });
|
||||
out.push(gl::wgl_extra::SAMPLES_ARB as c_int);
|
||||
out.push(multisampling as c_int);
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
out.push(gl::wgl_extra::STEREO_ARB as c_int);
|
||||
out.push(if reqs.stereoscopy { 1 } else { 0 });
|
||||
|
||||
if reqs.srgb {
|
||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() {
|
||||
out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int);
|
||||
out.push(1);
|
||||
} else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() {
|
||||
out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int);
|
||||
out.push(1);
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
match reqs.release_behavior {
|
||||
ReleaseBehavior::Flush => (),
|
||||
ReleaseBehavior::None => {
|
||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_context_flush_control").is_some() {
|
||||
out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int);
|
||||
out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
out.push(0);
|
||||
out
|
||||
};
|
||||
|
||||
let mut format_id = mem::uninitialized();
|
||||
let mut num_formats = mem::uninitialized();
|
||||
if extra.ChoosePixelFormatARB(hdc as *const _, descriptor.as_ptr(), ptr::null(), 1,
|
||||
&mut format_id, &mut num_formats) == 0
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if num_formats == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let get_info = |attrib: u32| {
|
||||
let mut value = mem::uninitialized();
|
||||
extra.GetPixelFormatAttribivARB(hdc as *const _, format_id as c_int,
|
||||
0, 1, [attrib as c_int].as_ptr(),
|
||||
&mut value);
|
||||
value as u32
|
||||
};
|
||||
|
||||
let pf_desc = PixelFormat {
|
||||
hardware_accelerated: get_info(gl::wgl_extra::ACCELERATION_ARB) !=
|
||||
gl::wgl_extra::NO_ACCELERATION_ARB,
|
||||
color_bits: get_info(gl::wgl_extra::RED_BITS_ARB) as u8 +
|
||||
get_info(gl::wgl_extra::GREEN_BITS_ARB) as u8 +
|
||||
get_info(gl::wgl_extra::BLUE_BITS_ARB) as u8,
|
||||
alpha_bits: get_info(gl::wgl_extra::ALPHA_BITS_ARB) as u8,
|
||||
depth_bits: get_info(gl::wgl_extra::DEPTH_BITS_ARB) as u8,
|
||||
stencil_bits: get_info(gl::wgl_extra::STENCIL_BITS_ARB) as u8,
|
||||
stereoscopy: get_info(gl::wgl_extra::STEREO_ARB) != 0,
|
||||
double_buffer: get_info(gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0,
|
||||
multisampling: {
|
||||
if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() {
|
||||
match get_info(gl::wgl_extra::SAMPLES_ARB) {
|
||||
0 => None,
|
||||
a => Some(a as u16),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
srgb: if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() {
|
||||
get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0
|
||||
} else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() {
|
||||
get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0
|
||||
} else {
|
||||
false
|
||||
},
|
||||
};
|
||||
|
||||
Ok((format_id, pf_desc))
|
||||
}
|
||||
|
||||
/// Calls `SetPixelFormat` on a window.
|
||||
unsafe fn set_pixel_format(hdc: winapi::HDC, id: c_int) -> Result<(), CreationError> {
|
||||
let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed();
|
||||
|
||||
if gdi32::DescribePixelFormat(hdc, id, mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>()
|
||||
as winapi::UINT, &mut output) == 0
|
||||
{
|
||||
return Err(CreationError::OsError(format!("DescribePixelFormat function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
if gdi32::SetPixelFormat(hdc, id, &output) == 0 {
|
||||
return Err(CreationError::OsError(format!("SetPixelFormat function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Loads the `opengl32.dll` library.
|
||||
unsafe fn load_opengl32_dll() -> Result<winapi::HMODULE, CreationError> {
|
||||
let name = OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let lib = kernel32::LoadLibraryW(name.as_ptr());
|
||||
|
||||
if lib.is_null() {
|
||||
return Err(CreationError::OsError(format!("LoadLibrary function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
Ok(lib)
|
||||
}
|
||||
|
||||
/// Loads the WGL functions that are not guaranteed to be supported.
|
||||
///
|
||||
/// The `window` must be passed because the driver can vary depending on the window's
|
||||
/// characteristics.
|
||||
unsafe fn load_extra_functions(window: winapi::HWND) -> Result<gl::wgl_extra::Wgl, CreationError> {
|
||||
let (ex_style, style) = (winapi::WS_EX_APPWINDOW, winapi::WS_POPUP |
|
||||
winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN);
|
||||
|
||||
// creating a dummy invisible window
|
||||
let dummy_window = {
|
||||
// getting the rect of the real window
|
||||
let rect = {
|
||||
let mut placement: winapi::WINDOWPLACEMENT = mem::zeroed();
|
||||
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT;
|
||||
if user32::GetWindowPlacement(window, &mut placement) == 0 {
|
||||
panic!();
|
||||
}
|
||||
placement.rcNormalPosition
|
||||
};
|
||||
|
||||
// getting the class name of the real window
|
||||
let mut class_name = [0u16; 128];
|
||||
if user32::GetClassNameW(window, class_name.as_mut_ptr(), 128) == 0 {
|
||||
return Err(CreationError::OsError(format!("GetClassNameW function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
// this dummy window should match the real one enough to get the same OpenGL driver
|
||||
let win = user32::CreateWindowExW(ex_style, class_name.as_ptr(),
|
||||
b"dummy window\0".as_ptr() as *const _, style,
|
||||
winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
ptr::null_mut(), ptr::null_mut(),
|
||||
kernel32::GetModuleHandleW(ptr::null()),
|
||||
ptr::null_mut());
|
||||
|
||||
if win.is_null() {
|
||||
return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
let hdc = user32::GetDC(win);
|
||||
if hdc.is_null() {
|
||||
let err = Err(CreationError::OsError(format!("GetDC function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
return err;
|
||||
}
|
||||
|
||||
WindowWrapper(win, hdc)
|
||||
};
|
||||
|
||||
// getting the pixel format that we will use and setting it
|
||||
{
|
||||
let id = try!(choose_dummy_pixel_format(dummy_window.1));
|
||||
try!(set_pixel_format(dummy_window.1, id));
|
||||
}
|
||||
|
||||
// creating the dummy OpenGL context and making it current
|
||||
let dummy_context = try!(create_context(None, dummy_window.0, dummy_window.1));
|
||||
let _current_context = try!(CurrentContextGuard::make_current(dummy_window.1,
|
||||
dummy_context.0));
|
||||
|
||||
// loading the extra WGL functions
|
||||
Ok(gl::wgl_extra::Wgl::load_with(|addr| {
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
gl::wgl::GetProcAddress(addr) as *const c_void
|
||||
}))
|
||||
}
|
||||
|
||||
/// This function chooses a pixel format that is likely to be provided by
|
||||
/// the main video driver of the system.
|
||||
fn choose_dummy_pixel_format(hdc: winapi::HDC) -> Result<c_int, CreationError> {
|
||||
// building the descriptor to pass to ChoosePixelFormat
|
||||
let descriptor = winapi::PIXELFORMATDESCRIPTOR {
|
||||
nSize: mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() as u16,
|
||||
nVersion: 1,
|
||||
dwFlags: winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_SUPPORT_OPENGL | winapi::PFD_DOUBLEBUFFER,
|
||||
iPixelType: winapi::PFD_TYPE_RGBA,
|
||||
cColorBits: 24,
|
||||
cRedBits: 0,
|
||||
cRedShift: 0,
|
||||
cGreenBits: 0,
|
||||
cGreenShift: 0,
|
||||
cBlueBits: 0,
|
||||
cBlueShift: 0,
|
||||
cAlphaBits: 8,
|
||||
cAlphaShift: 0,
|
||||
cAccumBits: 0,
|
||||
cAccumRedBits: 0,
|
||||
cAccumGreenBits: 0,
|
||||
cAccumBlueBits: 0,
|
||||
cAccumAlphaBits: 0,
|
||||
cDepthBits: 24,
|
||||
cStencilBits: 8,
|
||||
cAuxBuffers: 0,
|
||||
iLayerType: winapi::PFD_MAIN_PLANE,
|
||||
bReserved: 0,
|
||||
dwLayerMask: 0,
|
||||
dwVisibleMask: 0,
|
||||
dwDamageMask: 0,
|
||||
};
|
||||
|
||||
// now querying
|
||||
let pf_id = unsafe { gdi32::ChoosePixelFormat(hdc, &descriptor) };
|
||||
if pf_id == 0 {
|
||||
return Err(CreationError::OsError("No available pixel format".to_owned()));
|
||||
}
|
||||
|
||||
Ok(pf_id)
|
||||
}
|
||||
|
|
@ -9,15 +9,10 @@ use super::WindowState;
|
|||
use super::Window;
|
||||
use super::MonitorId;
|
||||
use super::WindowWrapper;
|
||||
use super::Context;
|
||||
|
||||
use Api;
|
||||
use CreationError;
|
||||
use CreationError::OsError;
|
||||
use CursorState;
|
||||
use GlAttributes;
|
||||
use GlRequest;
|
||||
use PixelFormatRequirements;
|
||||
use WindowAttributes;
|
||||
|
||||
use std::ffi::{OsStr};
|
||||
|
|
@ -29,28 +24,8 @@ use kernel32;
|
|||
use dwmapi;
|
||||
use user32;
|
||||
|
||||
use api::wgl::Context as WglContext;
|
||||
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());
|
||||
pub fn new_window(window: &WindowAttributes) -> Result<Window, CreationError> {
|
||||
let window = window.clone();
|
||||
let pf_reqs = pf_reqs.clone();
|
||||
let opengl = opengl.clone();
|
||||
|
||||
// initializing variables to be sent to the task
|
||||
|
||||
|
|
@ -64,7 +39,7 @@ pub fn new_window(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
|||
thread::spawn(move || {
|
||||
unsafe {
|
||||
// creating and sending the `Window`
|
||||
match init(title, &window, &pf_reqs, &opengl, egl) {
|
||||
match init(title, &window) {
|
||||
Ok(w) => tx.send(Ok(w)).ok(),
|
||||
Err(e) => {
|
||||
tx.send(Err(e)).ok();
|
||||
|
|
@ -90,17 +65,7 @@ pub fn new_window(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
|||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
unsafe fn init(title: Vec<u16>, window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
||||
opengl: &GlAttributes<RawContext>, egl: Option<Egl>)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
let opengl = opengl.clone().map_sharing(|sharelists| {
|
||||
match sharelists {
|
||||
RawContext::Wgl(c) => c,
|
||||
_ => unimplemented!()
|
||||
}
|
||||
});
|
||||
|
||||
unsafe fn init(title: Vec<u16>, window: &WindowAttributes) -> Result<Window, CreationError> {
|
||||
// registering the 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)
|
||||
};
|
||||
|
||||
// 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
|
||||
if window.transparent {
|
||||
let bb = winapi::DWM_BLURBEHIND {
|
||||
|
|
@ -240,7 +179,6 @@ unsafe fn init(title: Vec<u16>, window: &WindowAttributes, pf_reqs: &PixelFormat
|
|||
// building the struct
|
||||
Ok(Window {
|
||||
window: real_window,
|
||||
context: context,
|
||||
events_receiver: events_receiver,
|
||||
window_state: window_state,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,15 +10,9 @@ use std::sync::{
|
|||
};
|
||||
use std::sync::mpsc::Receiver;
|
||||
use libc;
|
||||
use ContextError;
|
||||
use {CreationError, Event, MouseCursor};
|
||||
use CursorState;
|
||||
use GlAttributes;
|
||||
use GlContext;
|
||||
|
||||
use Api;
|
||||
use PixelFormat;
|
||||
use PixelFormatRequirements;
|
||||
use WindowAttributes;
|
||||
|
||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
|
|
@ -27,12 +21,6 @@ use winapi;
|
|||
use user32;
|
||||
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 event;
|
||||
mod init;
|
||||
|
|
@ -58,9 +46,6 @@ pub struct Window {
|
|||
/// Main handle for the window.
|
||||
window: WindowWrapper,
|
||||
|
||||
/// OpenGL context.
|
||||
context: Context,
|
||||
|
||||
/// Receiver for the events dispatched by the window callback.
|
||||
events_receiver: Receiver<Event>,
|
||||
|
||||
|
|
@ -71,11 +56,6 @@ pub struct Window {
|
|||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
enum Context {
|
||||
Egl(EglContext),
|
||||
Wgl(WglContext),
|
||||
}
|
||||
|
||||
/// A simple wrapper that destroys the window when it is destroyed.
|
||||
// FIXME: remove `pub` (https://github.com/rust-lang/rust/issues/23585)
|
||||
#[doc(hidden)]
|
||||
|
|
@ -109,18 +89,8 @@ impl WindowProxy {
|
|||
|
||||
impl Window {
|
||||
/// See the docs in the crate root file.
|
||||
pub fn new(window: &WindowAttributes, pf_reqs: &PixelFormatRequirements,
|
||||
opengl: &GlAttributes<&Window>, egl: Option<&Egl>)
|
||||
-> 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)
|
||||
pub fn new(window: &WindowAttributes) -> Result<Window, CreationError> {
|
||||
init::new_window(window)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue