use crate::glx;
use x11::xlib::*;
use glx::types::GLXDrawable;
use sparkle::gl;
use std::ffi::CStr;
use std::os::raw::*;
use euclid::default::Size2D;
use crate::GLVersion;
use crate::NativeGLContext;
use crate::NativeGLContextHandle;
pub struct ScopedXFree<T> {
ptr: *mut T
}
impl<T> ScopedXFree<T> {
#[inline(always)]
pub fn new(ptr: *mut T) -> ScopedXFree<T> {
ScopedXFree {
ptr: ptr
}
}
#[inline(always)]
pub fn as_ptr(&self) -> *mut T {
self.ptr
}
}
impl<T> Drop for ScopedXFree<T> {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { XFree(self.ptr as *mut _); };
}
}
}
unsafe fn get_visual_and_depth(s: *mut Screen, id: VisualID) -> Result<(*mut Visual, c_int), &'static str> {
for d in 0..((*s).ndepths as isize) {
let depth_info : *mut Depth = (*s).depths.offset(d);
for v in 0..((*depth_info).nvisuals as isize) {
let visual : *mut Visual = (*depth_info).visuals.offset(v);
if (*visual).visualid == id {
return Ok((visual, (*depth_info).depth));
}
}
}
Err("Visual not on screen")
}
pub fn create_offscreen_pixmap_backed_context(size: Size2D<i32>,
shared_with: Option<&NativeGLContextHandle>,
api_type: &gl::GlType,
api_version: GLVersion) -> Result<NativeGLContext, &'static str> {
match *api_type {
gl::GlType::Gles => {
return Err("OpenGL ES is not supported");
},
_ => {}
}
let (shared_with, dpy) = match shared_with {
Some(handle) => (Some(&handle.0), handle.1),
None => {
let dpy = unsafe { XOpenDisplay(0 as *mut c_char) as *mut glx::types::Display };
if dpy.is_null() {
return Err("glx::XOpenDisplay");
}
(None, dpy)
}
};
let mut attributes = [
glx::DRAWABLE_TYPE as c_int, glx::PIXMAP_BIT as c_int,
glx::X_RENDERABLE as c_int, 1,
glx::RENDER_TYPE as c_int, glx::RGBA_BIT as c_int,
0 as c_int
];
let mut config_count : c_int = 0;
let configs = ScopedXFree::new(unsafe {
glx::ChooseFBConfig(dpy,
XDefaultScreen(dpy as *mut Display),
attributes.as_mut_ptr(),
&mut config_count)
});
if configs.as_ptr().is_null() {
return Err("glx::ChooseFBConfig");
}
debug_assert!(config_count > 0);
let mut config_index = 0;
let mut visual_id = glx::NONE as c_int;
for i in 0..(config_count as isize) {
unsafe {
let config = *configs.as_ptr().offset(i);
let mut drawable_type : c_int = 0;
if glx::GetFBConfigAttrib(dpy, config, glx::DRAWABLE_TYPE as c_int, &mut drawable_type) != 0
|| (drawable_type & (glx::PIXMAP_BIT as c_int) == 0) {
continue;
}
if glx::GetFBConfigAttrib(dpy, config, glx::VISUAL_ID as c_int, &mut visual_id) != 0
|| visual_id == 0 {
continue;
}
}
config_index = i;
break;
}
if visual_id == 0 {
return Err("We don't have any config with visuals");
}
unsafe {
let screen = XDefaultScreenOfDisplay(dpy as *mut _);
let screen_id = XDefaultScreen(dpy as * mut _);
let extensions = CStr::from_ptr(glx::QueryExtensionsString(dpy, screen_id)).to_bytes().to_vec();
let extensions = String::from_utf8(extensions).unwrap();
let (_, depth) = get_visual_and_depth(screen, visual_id as VisualID)?;
let pixmap = XCreatePixmap(dpy as *mut _,
XRootWindowOfScreen(screen),
size.width as c_uint,
size.height as c_uint,
depth as c_uint);
if pixmap == 0 {
return Err("XCreatePixMap");
}
let glx_pixmap = glx::CreatePixmap(dpy,
*configs.as_ptr().offset(config_index),
pixmap,
0 as *const c_int);
if glx_pixmap == 0 {
XFreePixmap(dpy as *mut _, pixmap);
return Err("glx::createPixmap");
}
let chosen_config = *configs.as_ptr().offset(config_index);
NativeGLContext::new(shared_with, api_version, dpy, glx_pixmap as GLXDrawable, chosen_config, extensions)
}
}