You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
9.7 KiB
302 lines
9.7 KiB
|
|
extern crate x11;
|
|
extern crate xcb;
|
|
extern crate gl;
|
|
extern crate libc;
|
|
|
|
use xcb::dri2;
|
|
|
|
use x11::xlib;
|
|
use x11::glx::*;
|
|
|
|
use std::ptr::null_mut;
|
|
use std::ffi::{CStr, CString};
|
|
use std::os::raw::{c_int, c_void};
|
|
|
|
|
|
const GLX_CONTEXT_MAJOR_VERSION_ARB: u32 = 0x2091;
|
|
const GLX_CONTEXT_MINOR_VERSION_ARB: u32 = 0x2092;
|
|
|
|
type GlXCreateContextAttribsARBProc =
|
|
unsafe extern "C" fn (dpy: *mut xlib::Display, fbc: GLXFBConfig,
|
|
share_context: GLXContext, direct: xlib::Bool,
|
|
attribs: *const c_int) -> GLXContext;
|
|
|
|
|
|
unsafe fn load_gl_func (name: &str) -> *mut c_void {
|
|
let cname = CString::new(name).unwrap();
|
|
let ptr: *mut c_void = std::mem::transmute(glXGetProcAddress(
|
|
cname.as_ptr() as *const u8
|
|
));
|
|
if ptr.is_null() {
|
|
panic!("could not load {}", name);
|
|
}
|
|
ptr
|
|
}
|
|
|
|
fn check_glx_extension(glx_exts: &str, ext_name: &str) -> bool {
|
|
for glx_ext in glx_exts.split(" ") {
|
|
if glx_ext == ext_name {
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
static mut ctx_error_occurred: bool = false;
|
|
unsafe extern "C" fn ctx_error_handler(
|
|
_dpy: *mut xlib::Display,
|
|
_ev: *mut xlib::XErrorEvent) -> i32 {
|
|
ctx_error_occurred = true;
|
|
0
|
|
}
|
|
|
|
|
|
unsafe fn check_gl_error() {
|
|
let err = gl::GetError();
|
|
if err != gl::NO_ERROR {
|
|
println!("got gl error {}", err);
|
|
}
|
|
}
|
|
|
|
// returns the glx version in a decimal form
|
|
// eg. 1.3 => 13
|
|
fn glx_dec_version(dpy: *mut xlib::Display) -> i32 {
|
|
let mut maj: c_int = 0;
|
|
let mut min: c_int = 0;
|
|
unsafe {
|
|
if glXQueryVersion(dpy,
|
|
&mut maj as *mut c_int,
|
|
&mut min as *mut c_int) == 0 {
|
|
panic!("cannot get glx version");
|
|
}
|
|
}
|
|
(maj*10 + min) as i32
|
|
}
|
|
|
|
|
|
fn get_glxfbconfig(dpy: *mut xlib::Display, screen_num: i32,
|
|
visual_attribs: &[i32]) -> GLXFBConfig {
|
|
unsafe {
|
|
let mut fbcount: c_int = 0;
|
|
let fbcs = glXChooseFBConfig(dpy, screen_num,
|
|
visual_attribs.as_ptr(),
|
|
&mut fbcount as *mut c_int);
|
|
|
|
if fbcount == 0 {
|
|
panic!("could not find compatible fb config");
|
|
}
|
|
// we pick the first from the list
|
|
let fbc = *fbcs;
|
|
xlib::XFree(fbcs as *mut c_void);
|
|
fbc
|
|
}
|
|
}
|
|
|
|
|
|
fn main() { unsafe {
|
|
let (conn, screen_num) = xcb::Connection::connect_with_xlib_display().unwrap();
|
|
conn.set_event_queue_owner(xcb::EventQueueOwner::Xcb);
|
|
|
|
if glx_dec_version(conn.get_raw_dpy()) < 13 {
|
|
panic!("glx-1.3 is not supported");
|
|
}
|
|
|
|
let fbc = get_glxfbconfig(conn.get_raw_dpy(), screen_num, &[
|
|
GLX_X_RENDERABLE , 1,
|
|
GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
|
|
GLX_RENDER_TYPE , GLX_RGBA_BIT,
|
|
GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
|
|
GLX_RED_SIZE , 8,
|
|
GLX_GREEN_SIZE , 8,
|
|
GLX_BLUE_SIZE , 8,
|
|
GLX_ALPHA_SIZE , 8,
|
|
GLX_DEPTH_SIZE , 24,
|
|
GLX_STENCIL_SIZE , 8,
|
|
GLX_DOUBLEBUFFER , 1,
|
|
0
|
|
]);
|
|
|
|
let vi: *const xlib::XVisualInfo =
|
|
glXGetVisualFromFBConfig(conn.get_raw_dpy(), fbc);
|
|
|
|
let dri2_ev = {
|
|
conn.prefetch_extension_data(dri2::id());
|
|
match conn.get_extension_data(dri2::id()) {
|
|
None => { panic!("could not load dri2 extension") },
|
|
Some(r) => { r.first_event() }
|
|
}
|
|
};
|
|
|
|
let (wm_protocols, wm_delete_window) = {
|
|
let pc = xcb::intern_atom(&conn, false, "WM_PROTOCOLS");
|
|
let dwc = xcb::intern_atom(&conn, false, "WM_DELETE_WINDOW");
|
|
|
|
let p = match pc.get_reply() {
|
|
Ok(p) => p.atom(),
|
|
Err(_) => panic!("could not load WM_PROTOCOLS atom")
|
|
};
|
|
let dw = match dwc.get_reply() {
|
|
Ok(dw) => dw.atom(),
|
|
Err(_) => panic!("could not load WM_DELETE_WINDOW atom")
|
|
};
|
|
(p, dw)
|
|
};
|
|
|
|
let setup = conn.get_setup();
|
|
let screen = setup.roots().nth((*vi).screen as usize).unwrap();
|
|
|
|
let cmap = conn.generate_id();
|
|
let win = conn.generate_id();
|
|
|
|
xcb::create_colormap(&conn, xcb::COLORMAP_ALLOC_NONE as u8,
|
|
cmap, screen.root(), (*vi).visualid as u32);
|
|
|
|
let cw_values = [
|
|
(xcb::CW_BACK_PIXEL, screen.white_pixel()),
|
|
(xcb::CW_BORDER_PIXEL, screen.black_pixel()),
|
|
(xcb::CW_EVENT_MASK,
|
|
xcb::EVENT_MASK_KEY_PRESS | xcb::EVENT_MASK_EXPOSURE),
|
|
(xcb::CW_COLORMAP, cmap)
|
|
];
|
|
|
|
xcb::create_window(&conn, (*vi).depth as u8, win, screen.root(), 0, 0, 640, 480,
|
|
0, xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
|
|
(*vi).visualid as u32, &cw_values);
|
|
|
|
xlib::XFree(vi as *mut c_void);
|
|
|
|
let title = "XCB OpenGL";
|
|
xcb::change_property(&conn,
|
|
xcb::PROP_MODE_REPLACE as u8,
|
|
win,
|
|
xcb::ATOM_WM_NAME,
|
|
xcb::ATOM_STRING,
|
|
8, title.as_bytes());
|
|
|
|
let protocols = [wm_delete_window];
|
|
xcb::change_property(&conn, xcb::PROP_MODE_REPLACE as u8,
|
|
win, wm_protocols, xcb::ATOM_ATOM, 32, &protocols);
|
|
|
|
xcb::map_window(&conn, win);
|
|
conn.flush();
|
|
xlib::XSync(conn.get_raw_dpy(), xlib::False);
|
|
|
|
let glx_exts = CStr::from_ptr(
|
|
glXQueryExtensionsString(conn.get_raw_dpy(), screen_num))
|
|
.to_str().unwrap();
|
|
|
|
if !check_glx_extension(&glx_exts, "GLX_ARB_create_context") {
|
|
panic!("could not find GLX extension GLX_ARB_create_context");
|
|
}
|
|
|
|
// with glx, no need of a current context is needed to load symbols
|
|
// otherwise we would need to create a temporary legacy GL context
|
|
// for loading symbols (at least glXCreateContextAttribsARB)
|
|
let glx_create_context_attribs: GlXCreateContextAttribsARBProc =
|
|
std::mem::transmute(load_gl_func("glXCreateContextAttribsARB"));
|
|
|
|
// loading all other symbols
|
|
gl::load_with(|n| load_gl_func(&n));
|
|
|
|
if !gl::GenVertexArrays::is_loaded() {
|
|
panic!("no GL3 support available!");
|
|
}
|
|
|
|
// installing an event handler to check if error is generated
|
|
ctx_error_occurred = false;
|
|
let old_handler = xlib::XSetErrorHandler(Some(ctx_error_handler));
|
|
|
|
let context_attribs: [c_int; 5] = [
|
|
GLX_CONTEXT_MAJOR_VERSION_ARB as c_int, 3,
|
|
GLX_CONTEXT_MINOR_VERSION_ARB as c_int, 0,
|
|
0
|
|
];
|
|
let ctx = glx_create_context_attribs(conn.get_raw_dpy(), fbc, null_mut(),
|
|
xlib::True, &context_attribs[0] as *const c_int);
|
|
|
|
conn.flush();
|
|
xlib::XSync(conn.get_raw_dpy(), xlib::False);
|
|
xlib::XSetErrorHandler(std::mem::transmute(old_handler));
|
|
|
|
if ctx.is_null() || ctx_error_occurred {
|
|
panic!("error when creating gl-3.0 context");
|
|
}
|
|
|
|
if glXIsDirect(conn.get_raw_dpy(), ctx) == 0 {
|
|
panic!("obtained indirect rendering context")
|
|
}
|
|
|
|
loop {
|
|
if let Some(ev) = conn.wait_for_event() {
|
|
let ev_type = ev.response_type() & !0x80;
|
|
match ev_type {
|
|
xcb::EXPOSE => {
|
|
glXMakeCurrent(conn.get_raw_dpy(), win as xlib::XID, ctx);
|
|
gl::ClearColor(0.5f32, 0.5f32, 1.0f32, 1.0f32);
|
|
gl::Clear(gl::COLOR_BUFFER_BIT);
|
|
gl::Flush();
|
|
check_gl_error();
|
|
glXSwapBuffers(conn.get_raw_dpy(), win as xlib::XID);
|
|
glXMakeCurrent(conn.get_raw_dpy(), 0, null_mut());
|
|
},
|
|
xcb::KEY_PRESS => {
|
|
break;
|
|
},
|
|
xcb::CLIENT_MESSAGE => {
|
|
let cmev = unsafe {
|
|
xcb::cast_event::<xcb::ClientMessageEvent>(&ev)
|
|
};
|
|
if cmev.type_() == wm_protocols && cmev.format() == 32 {
|
|
let protocol = cmev.data().data32()[0];
|
|
if protocol == wm_delete_window {
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
_ => {
|
|
// following stuff is not obvious at all, but is necessary
|
|
// to handle GL when XCB owns the event queue
|
|
if ev_type == dri2_ev || ev_type == dri2_ev+1 {
|
|
// these are libgl dri2 event that need special handling
|
|
// see https://bugs.freedesktop.org/show_bug.cgi?id=35945#c4
|
|
// and mailing thread starting here:
|
|
// http://lists.freedesktop.org/archives/xcb/2015-November/010556.html
|
|
|
|
if let Some(proc_) =
|
|
xlib::XESetWireToEvent(conn.get_raw_dpy(),
|
|
ev_type as i32, None) {
|
|
xlib::XESetWireToEvent(conn.get_raw_dpy(),
|
|
ev_type as i32, Some(proc_));
|
|
let raw_ev = ev.ptr;
|
|
(*raw_ev).sequence =
|
|
xlib::XLastKnownRequestProcessed(
|
|
conn.get_raw_dpy()) as u16;
|
|
let mut dummy: xlib::XEvent = std::mem::zeroed();
|
|
proc_(conn.get_raw_dpy(),
|
|
&mut dummy as *mut xlib::XEvent,
|
|
raw_ev as *mut xlib::xEvent);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
conn.flush();
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// only to make sure that rs_client generate correct names for DRI2
|
|
// (used to be "*_DRI_2_*")
|
|
// should be in a "compile tests" section instead of example
|
|
let _ = xcb::ffi::dri2::XCB_DRI2_ATTACHMENT_BUFFER_ACCUM;
|
|
|
|
glXDestroyContext(conn.get_raw_dpy(), ctx);
|
|
|
|
xcb::unmap_window(&conn, win);
|
|
xcb::destroy_window(&conn, win);
|
|
xcb::free_colormap(&conn, cmap);
|
|
conn.flush();
|
|
}}
|