Browse Source

Add some xcb examples.

Include a shm example which I created by analyse the C exmaples and
try to reproduce with rust libc and rust-xcb.
master
Georg Hopp 6 years ago
parent
commit
ade2a66b37
Signed by: ghopp GPG Key ID: 4C5D226768784538
  1. 11
      xcb-test/Cargo.toml
  2. 132
      xcb-test/alternatives/info.rs
  3. 302
      xcb-test/alternatives/opengl.rs
  4. 202
      xcb-test/src/main.rs

11
xcb-test/Cargo.toml

@ -0,0 +1,11 @@
[package]
name = "xcb-test"
version = "0.1.0"
authors = ["Georg Hopp <georg@steffers.org>"]
edition = "2018"
[dependencies]
libc = "0.2"
gl = "0.5.2"
x11 = { version = "2.3", features = ["glx"] }
xcb = { version = "0.8", features = ["dri2", "randr", "thread", "xlib_xcb", "shm"] }

132
xcb-test/alternatives/info.rs

@ -0,0 +1,132 @@
extern crate xcb;
use std::iter::{Iterator};
use xcb::randr;
fn main() {
let dpy = ":0";
let (conn, screen_num) = xcb::Connection::connect(Some(&dpy)).unwrap();
let setup = conn.get_setup();
let screen = setup.roots().nth(screen_num as usize).unwrap();
println!("");
println!("Informations of screen {}:", screen.root());
println!(" width..........: {}", screen.width_in_pixels());
println!(" height.........: {}", screen.height_in_pixels());
println!(" white pixel....: {:x}", screen.white_pixel());
println!(" black pixel....: {:x}", screen.black_pixel());
let window_dummy = conn.generate_id();
xcb::create_window(
&conn, 0, window_dummy, screen.root()
, 0, 0, 1, 1, 0, 0, 0, &[]);
conn.flush();
let cookie = randr::get_screen_info(&conn, window_dummy);
let reply = cookie.get_reply().unwrap();
let sizes = reply.sizes();
for (i, size) in sizes.enumerate() {
if i != 0 { println!(""); }
println!("size of screen {}:", i+1);
println!(" {} x {} ({}mm x {}mm)", size.width(), size.height(),
size.mwidth(), size.mheight());
}
// ====
let window = conn.generate_id();
let values = [
(xcb::CW_BACK_PIXEL, screen.white_pixel()),
(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_KEY_PRESS),
];
xcb::create_window(&conn,
xcb::COPY_FROM_PARENT as u8,
window,
screen.root(),
0, 0,
150, 150,
10,
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
screen.root_visual(),
&values);
xcb::map_window(&conn, window);
let title = "Basic Window";
// setting title
xcb::change_property(&conn, xcb::PROP_MODE_REPLACE as u8, window,
xcb::ATOM_WM_NAME, xcb::ATOM_STRING, 8, title.as_bytes());
conn.flush();
// retrieving title
let cookie = xcb::get_property(&conn, false, window, xcb::ATOM_WM_NAME,
xcb::ATOM_STRING, 0, 1024);
if let Ok(reply) = cookie.get_reply() {
assert_eq!(std::str::from_utf8(reply.value()).unwrap(), title);
} else {
panic!("could not retrieve window title!");
}
// retrieving a few atoms
let (wm_state, wm_state_maxv, wm_state_maxh) = {
let cook = xcb::intern_atom(&conn, true, "_NET_WM_STATE");
let cook_maxv = xcb::intern_atom(&conn, true, "_NET_WM_STATE_MAXIMIZED_VERT");
let cook_maxh = xcb::intern_atom(&conn, true, "_NET_WM_STATE_MAXIMIZED_HORZ");
(cook.get_reply().unwrap().atom(),
cook_maxv.get_reply().unwrap().atom(),
cook_maxh.get_reply().unwrap().atom())
};
let mut maximized = false;
loop {
let event = conn.wait_for_event();
match event {
None => { break; }
Some(event) => {
let r = event.response_type();
if r == xcb::KEY_PRESS as u8 {
let key_press : &xcb::KeyPressEvent = unsafe {
xcb::cast_event(&event)
};
println!("Key '{}' pressed", key_press.detail());
if key_press.detail() == 0x3a { // M (on qwerty)
// toggle maximized
println!("toggle maximized: {} {}", wm_state_maxv, wm_state_maxh);
// ClientMessageData is a memory safe untagged union
let data = xcb::ClientMessageData::from_data32([
if maximized { 0 } else { 1 },
wm_state_maxv, wm_state_maxh,
0, 0
]);
let ev = xcb::ClientMessageEvent::new(32, window,
wm_state, data);
xcb::send_event(&conn, false, screen.root(),
xcb::EVENT_MASK_STRUCTURE_NOTIFY, &ev);
conn.flush();
maximized = !maximized;
}
else if key_press.detail() == 0x18 { // Q (on qwerty)
break;
}
}
}
}
}
}

302
xcb-test/alternatives/opengl.rs

@ -0,0 +1,302 @@
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();
}}

202
xcb-test/src/main.rs

@ -0,0 +1,202 @@
extern crate xcb;
use std::iter::{Iterator};
use std::{thread, time};
use std::sync::Arc;
use std::ptr::{null, null_mut};
use std::slice::from_raw_parts_mut;
pub fn getshm<'a>(size :usize) -> (i32, &'a mut [u32]) {
unsafe {
let id = libc::shmget( libc::IPC_PRIVATE
, size * 4
, libc::IPC_CREAT | 0o744 );
let ptr = libc::shmat(id, null(), 0);
(id as i32, from_raw_parts_mut(ptr as *mut u32, size))
}
}
fn main() {
let points: &[xcb::Point] = &[ xcb::Point::new(10, 10)
, xcb::Point::new(10, 20)
, xcb::Point::new(20, 10)
, xcb::Point::new(20, 20) ];
let polyline: &[xcb::Point] = &[ xcb::Point::new(50, 10 )
// rest of points are relative
, xcb::Point::new( 5, 20 )
, xcb::Point::new(25, -20)
, xcb::Point::new(10, 10 ) ];
let segments: &[xcb::Segment] = &[ xcb::Segment::new(100, 10, 140, 30)
, xcb::Segment::new(110, 25, 130, 60) ];
let rectangles: &[xcb::Rectangle]
= &[ xcb::Rectangle::new(10, 50, 40, 20)
, xcb::Rectangle::new(80, 50, 10, 40) ];
let arcs: &[xcb::Arc] = &[ xcb::Arc::new(10, 100, 60, 40, 0, 90 << 6)
, xcb::Arc::new(90, 100, 55, 40, 0, 270 << 6) ];
let (conn, screen_num) = {
let (conn, screen_num) = xcb::Connection::connect(None).unwrap();
(Arc::new(conn), screen_num)
};
let setup = conn.get_setup();
let screen = setup.roots().nth(screen_num as usize).unwrap();
let (shmid, shm) = getshm(150 * 150);
let shmseg = conn.generate_id();
xcb::shm::attach(&conn, shmseg, shmid as u32, false);
unsafe { libc::shmctl(shmid, libc::IPC_RMID, null_mut()); }
let foreground = conn.generate_id();
let pix = conn.generate_id();
xcb::create_gc( &conn
, foreground
, screen.root()
, &[ (xcb::GC_FOREGROUND, screen.black_pixel())
, (xcb::GC_GRAPHICS_EXPOSURES, 0) ] );
let window = conn.generate_id();
let values = [ ( xcb::CW_BACK_PIXEL, screen.white_pixel() )
, ( xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE
| xcb::EVENT_MASK_KEY_PRESS
| xcb::EVENT_MASK_STRUCTURE_NOTIFY
| xcb::EVENT_MASK_PROPERTY_CHANGE ) ];
xcb::create_window( &conn
, xcb::COPY_FROM_PARENT as u8
, window
, screen.root()
, 0, 0, 150, 150, 0
, xcb::WINDOW_CLASS_INPUT_OUTPUT as u16
, screen.root_visual()
, &values);
xcb::shm::create_pixmap( &conn
, pix
, window
, 150, 150
, screen.root_depth()
, shmseg
, 0 );
xcb::map_window(&conn, window);
{
let conn = conn.clone();
thread::spawn(move || {
let mut blink = false;
let mut i = 0;
loop {
let title = if blink {
"Basic Threaded Window ;-)"
} else {
"Basic Threaded Window :-)"
};
shm[i] = 0xFFFFFF;
i = (i + 1) % (150 * 150);
let c = xcb::change_property_checked(
&conn
, xcb::PROP_MODE_REPLACE as u8
, window
, xcb::ATOM_WM_NAME
, xcb::ATOM_STRING
, 8
, title.as_bytes() );
xcb::copy_area( &conn
, pix
, window
, foreground
, 0, 0, 0, 0
, 150, 150);
if conn.has_error().is_err() || c.request_check().is_err() {
break;
}
blink = !blink;
thread::sleep(time::Duration::from_millis(500));
}
});
}
conn.flush();
loop {
let event = conn.wait_for_event();
match event {
None => break,
Some(event) => {
match event.response_type() & !0x80 {
xcb::PROPERTY_NOTIFY => {
let prop_notify: &xcb::PropertyNotifyEvent = unsafe {
xcb::cast_event(&event)
};
if prop_notify.atom() == xcb::ATOM_WM_NAME {
// retrieving title
let cookie = xcb::get_property( &conn
, false
, window
, xcb::ATOM_WM_NAME
, xcb::ATOM_STRING
, 0, 1024);
if let Ok(reply) = cookie.get_reply() {
let r = reply.value();
let r = std::str::from_utf8(r).unwrap();
println!( "title changed to \"{}\"", r);
}
}
},
xcb::EXPOSE => {
xcb::poly_point( &conn
, xcb::COORD_MODE_ORIGIN as u8
, window
, foreground
, &points );
xcb::poly_line( &conn
, xcb::COORD_MODE_PREVIOUS as u8
, window
, foreground
, &polyline );
xcb::poly_segment( &conn
, window
, foreground
, &segments );
xcb::poly_rectangle( &conn
, window
, foreground
, &rectangles );
xcb::poly_arc( &conn
, window
, foreground
, &arcs );
conn.flush();
},
xcb::KEY_PRESS => {
let key_press: &xcb::KeyPressEvent = unsafe {
xcb::cast_event(&event)
};
println!("Key '{}' pressed", key_press.detail());
if key_press.detail() == 0x18 { // Q (on qwerty)
break;
}
},
_ => {},
}
}
}
}
}
Loading…
Cancel
Save