Skip to content

Instantly share code, notes, and snippets.

@saethlin
Created July 22, 2017 18:53
Show Gist options
  • Save saethlin/234e3f9a3a1f56a0f034d9788dff9976 to your computer and use it in GitHub Desktop.
Save saethlin/234e3f9a3a1f56a0f034d9788dff9976 to your computer and use it in GitHub Desktop.
Rust-XCB demo
extern crate xcb; // Make sure you add xcb to your Caro.toml dependencies
fn main() {
// First thing you have to do is get a connection. You can pass an argument
// but if you just leave it as None you'll connect to the display device you're currently on
let (connection, screen_num) = xcb::Connection::connect(None).unwrap();
let setup = connection.get_setup();
let screen = setup.roots().nth(screen_num as usize).unwrap();
// Now we have a connection to the X server (remember X does things sort of backwards,
// the client is the far away machine, the server is where you are).
// To make anything of it, we need to create an ID. This is like allocating memory for a variable.
let window = connection.generate_id();
// And with an ID created, we need to do a bit more voodoo before we can make a window:
// This is an array that identifies a few properties of the window.
// I think the first one is some drawing color defaults, and the second element is
// an event mask that determines which events the client will send to the server.
// Sometimes this is called registering for events
let values = [
(xcb::CW_BACK_PIXEL, screen.white_pixel()),
(
xcb::CW_EVENT_MASK,
xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_KEY_PRESS,
),
];
// Now to build a window on that connection:
xcb::create_window(
&connection,
xcb::COPY_FROM_PARENT as u8,
window,
screen.root(),
0,
0, // window origin x an y, I can't figure out if this actually does anything
800, // window width
500, // window height
10, // border size, I'm not convinced this does anything either
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
screen.root_visual(),
&values,
);
// Note that this call doesn't return _or_ mutate anything.
// Welcome to X, where nothing makes sense and everyone authenticates with `MIT-MAGIC-COOKIE-1`.
// Now we need to map the window to the screen, then flush the connection to be sure something happens.
xcb::map_window(&connection, window);
connection.flush();
// At this point the window just goes out of scope, so we need to actually do something to keep it alive.
// You could just sit in an infinite loop here, but might as well do something too
// Enter the event loop (Note: I advise you asbstract this away with an iterator over events)
loop {
let event = connection.wait_for_event();
match event {
None => { break; } // The window has been closed and/or something went wrong
Some(event) => {
match event.response_type() {
xcb::EXPOSE => {
println!("A wild window appeared!");
}
xcb::KEY_PRESS => {
// You need to cast between event types here
// The origin of that requirement is that C uses pointers to structs to fake polymorphism
// Unfortunately since we're using bindings we sort of inherit that but you can just
// ignore the casting unless there's some attribute you need that isn't on the normal event struct
let key_press : &xcb::KeyPressEvent = unsafe {
xcb::cast_event(&event)
};
// This 9 is the key code for the escape key on my keyboard. If you actually want to
// check if the escape key was pressed you need to use another part of xcb to load
// a key mapping that will convert dumb keycodes into a locale-specific key.
if key_press.detail() == 9 {
break;
}
}
_ => {}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment