use xcb::ffi::xcb_generic_event_t;
use xcb::{self, dri2, glx, x};
use xcb::{Raw, Xid};
use x11::glx::*;
use x11::xlib;
use std::ffi::{CStr, CString};
use std::os::raw::{c_int, c_void};
use std::ptr;
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_OCCURED: bool = false;
unsafe extern "C" fn ctx_error_handler(
_dpy: *mut xlib::Display,
_ev: *mut xlib::XErrorEvent,
) -> i32 {
CTX_ERROR_OCCURED = true;
0
}
unsafe fn check_gl_error() {
let err = gl::GetError();
if err != gl::NO_ERROR {
println!("got gl error {}", err);
}
}
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");
}
let fbc = *fbcs;
xlib::XFree(fbcs as *mut c_void);
fbc
}
}
fn main() -> xcb::Result<()> {
let (conn, screen_num) =
xcb::Connection::connect_with_xlib_display_and_extensions(&[], &[xcb::Extension::Dri2])?;
conn.set_event_queue_owner(xcb::EventQueueOwner::Xcb);
let glx_ver = conn.wait_for_reply(conn.send_request(&glx::QueryVersion {
major_version: 1,
minor_version: 3,
}))?;
assert!(glx_ver.major_version() >= 1 && glx_ver.minor_version() >= 3);
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_ptr: *mut xlib::XVisualInfo =
unsafe { glXGetVisualFromFBConfig(conn.get_raw_dpy(), fbc) };
let vi = unsafe { *vi_ptr };
let (wm_protocols, wm_del_window) = {
let cookies = (
conn.send_request(&x::InternAtom {
only_if_exists: false,
name: b"WM_PROTOCOLS",
}),
conn.send_request(&x::InternAtom {
only_if_exists: false,
name: b"WM_DELETE_WINDOW",
}),
);
(
conn.wait_for_reply(cookies.0)?.atom(),
conn.wait_for_reply(cookies.1)?.atom(),
)
};
let setup = conn.get_setup();
let screen = setup.roots().nth(vi.screen as usize).unwrap();
let cmap: x::Colormap = conn.generate_id();
let win: x::Window = conn.generate_id();
conn.send_request(&x::CreateColormap {
alloc: x::ColormapAlloc::None,
mid: cmap,
window: screen.root(),
visual: vi.visualid as u32,
});
conn.send_request(&x::CreateWindow {
depth: x::COPY_FROM_PARENT as u8,
wid: win,
parent: screen.root(),
x: 0,
y: 0,
width: 640,
height: 480,
border_width: 0,
class: x::WindowClass::InputOutput,
visual: vi.visualid as u32,
value_list: &[
x::Cw::BackPixel(screen.white_pixel()),
x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS),
x::Cw::Colormap(cmap),
],
});
unsafe {
xlib::XFree(vi_ptr as *mut c_void);
}
let title = "XCB OpenGL";
conn.check_request(conn.send_request_checked(&x::ChangeProperty {
mode: x::PropMode::Replace,
window: win,
property: x::ATOM_WM_NAME,
r#type: x::ATOM_STRING,
data: title.as_bytes(),
}))?;
conn.check_request(conn.send_request_checked(&x::ChangeProperty {
mode: x::PropMode::Replace,
window: win,
property: wm_protocols,
r#type: x::ATOM_ATOM,
data: &[wm_del_window],
}))?;
conn.check_request(conn.send_request_checked(&x::MapWindow { window: win }))?;
unsafe {
xlib::XSync(conn.get_raw_dpy(), xlib::False);
}
let glx_exts =
unsafe { 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");
}
let glx_create_context_attribs: GlXCreateContextAttribsARBProc =
unsafe { std::mem::transmute(load_gl_func("glXCreateContextAttribsARB")) };
unsafe {
gl::load_with(|n| load_gl_func(&n));
}
if !gl::GenVertexArrays::is_loaded() {
panic!("no GL3 support available!");
}
unsafe {
CTX_ERROR_OCCURED = false;
}
let old_handler = unsafe { 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 = unsafe {
glx_create_context_attribs(
conn.get_raw_dpy(),
fbc,
ptr::null_mut(),
xlib::True,
&context_attribs[0] as *const c_int,
)
};
conn.flush()?;
unsafe {
xlib::XSync(conn.get_raw_dpy(), xlib::False);
xlib::XSetErrorHandler(std::mem::transmute(old_handler));
}
if ctx.is_null() || unsafe { CTX_ERROR_OCCURED } {
panic!("error when creating gl-3.0 context");
}
if unsafe { glXIsDirect(conn.get_raw_dpy(), ctx) } == 0 {
panic!("obtained indirect rendering context")
}
loop {
conn.flush()?;
match conn.wait_for_event()? {
xcb::Event::X(x::Event::Expose(_)) => unsafe {
glXMakeCurrent(conn.get_raw_dpy(), win.resource_id() 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.resource_id() as xlib::XID);
glXMakeCurrent(conn.get_raw_dpy(), 0, ptr::null_mut());
},
xcb::Event::X(x::Event::KeyPress(_ev)) => {}
xcb::Event::X(x::Event::ClientMessage(ev)) => {
if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
if atom == wm_del_window.resource_id() {
break;
}
}
}
xcb::Event::Dri2(dri2::Event::BufferSwapComplete(ev)) => unsafe {
rewire_event(&conn, ev.as_raw())
},
xcb::Event::Dri2(dri2::Event::InvalidateBuffers(ev)) => unsafe {
rewire_event(&conn, ev.as_raw())
},
_ => {}
}
}
unsafe {
glXDestroyContext(conn.get_raw_dpy(), ctx);
}
conn.send_request(&x::UnmapWindow { window: win });
conn.send_request(&x::DestroyWindow { window: win });
conn.send_request(&x::FreeColormap { cmap });
conn.flush()?;
Ok(())
}
unsafe fn rewire_event(conn: &xcb::Connection, raw_ev: *mut xcb_generic_event_t) {
let ev_type = ((*raw_ev).response_type & 0x7f) as i32;
if let Some(proc) = xlib::XESetWireToEvent(conn.get_raw_dpy(), ev_type, None) {
xlib::XESetWireToEvent(conn.get_raw_dpy(), ev_type, Some(proc));
(*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,
);
}
}