Crate xcb

source · []
Expand description

Rust bindings to the XCB library.

The X protocol C-language Binding (XCB - https://xcb.freedesktop.org/) is a replacement for Xlib featuring a small footprint, latency hiding, direct access to the protocol, improved threading support, and extensibility.

The communication is established with the X server by the creation of a Connection object.

A client communicates with the server by sending requests. There are 2 types of requests:

Requests are passed to the server by filling a request structure e.g. x::CreateWindow and passing it to Connection::send_request.

The server can also communicate with clients by sending Events. The client listens to events with calls such as Connection::wait_for_event (blocking) or Connection::poll_for_event (non-blocking).

The x module contains definitions of the core X protocol. Each extension is defined in its own module such as xkb or render, and is activated by a cargo feature of the same name.

Example

Here is a walk-through of a simple xcb client.

// we import the necessary modules (only the core X module in this application).
use xcb::{x};
// we need to import the `Xid` trait for the `resource_id` call down there.
use xcb::{Xid};

// Many xcb functions return a `xcb::Result` or compatible result.
fn main() -> xcb::Result<()> {
    // Connect to the X server.
    let (conn, screen_num) = xcb::Connection::connect(None)?;

    // Fetch the `x::Setup` and get the main `x::Screen` object.
    let setup = conn.get_setup();
    let screen = setup.roots().nth(screen_num as usize).unwrap();

    // Generate an `Xid` for the client window.
    // The type inference is needed here.
    let window: x::Window = conn.generate_id();

    // We can now create a window. For this we pass a `Request`
    // object to the `send_request_checked` method. The method
    // returns a cookie that will be used to check for success.
    let cookie = conn.send_request_checked(&x::CreateWindow {
        depth: x::COPY_FROM_PARENT as u8,
        wid: window,
        parent: screen.root(),
        x: 0,
        y: 0,
        width: 150,
        height: 150,
        border_width: 0,
        class: x::WindowClass::InputOutput,
        visual: screen.root_visual(),
        // this list must be in same order than `Cw` enum order
        value_list: &[
            x::Cw::BackPixel(screen.white_pixel()),
            x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS)
        ],
    });
    // We now check if the window creation worked.
    // A cookie can't be cloned; it is moved to the function.
    conn.check_request(cookie)?;

    // Let's change the window title
    let cookie = conn.send_request_checked(&x::ChangeProperty {
        mode: x::PropMode::Replace,
        window,
        property: x::ATOM_WM_NAME,
        r#type: x::ATOM_STRING,
        data: b"My XCB Window",
    });
    // And check for success again
    conn.check_request(cookie);

    // We now show ("map" in X terminology) the window.
    // This time we do not check for success, so we discard the cookie.
    conn.send_request(&x::MapWindow {
        window,
    });

    // We need a few atoms for our application.
    // We send a few requests in a row and wait for the replies after.
    let (wm_protocols, wm_del_window, wm_state, wm_state_maxv, wm_state_maxh) = {
        let cookies = (
            conn.send_request(&x::InternAtom {
                only_if_exists: true,
                name: b"WM_PROTOCOLS",
            }),
            conn.send_request(&x::InternAtom {
                only_if_exists: true,
                name: b"WM_DELETE_WINDOW",
            }),
            conn.send_request(&x::InternAtom {
                only_if_exists: true,
                name: b"_NET_WM_STATE",
            }),
            conn.send_request(&x::InternAtom {
                only_if_exists: true,
                name: b"_NET_WM_STATE_MAXIMIZED_VERT",
            }),
            conn.send_request(&x::InternAtom {
                only_if_exists: true,
                name: b"_NET_WM_STATE_MAXIMIZED_HORZ",
            }),
        );
        (
            conn.wait_for_reply(cookies.0)?.atom(),
            conn.wait_for_reply(cookies.1)?.atom(),
            conn.wait_for_reply(cookies.2)?.atom(),
            conn.wait_for_reply(cookies.3)?.atom(),
            conn.wait_for_reply(cookies.4)?.atom(),
        )
    };

    // We now activate the window close event by sending the following request.
    // If we don't do this we can still close the window by clicking on the "x" button,
    // but the event loop is notified through a connection shutdown error.
    conn.check_request(conn.send_request_checked(&x::ChangeProperty {
        mode: x::PropMode::Replace,
        window,
        property: wm_protocols,
        r#type: x::ATOM_ATOM,
        data: &[wm_del_window],
    }))?;

    // Previous request was checked, so a flush is not necessary in this case.
    // Otherwise, here is how to perform a connection flush.
    conn.flush()?;

    let mut maximized = false;

    // We enter the main event loop
    loop {
        match conn.wait_for_event()? {
            xcb::Event::X(x::Event::KeyPress(ev)) => {
                if ev.detail() == 0x3a {
                    // The M key was pressed
                    // (M only on qwerty keyboards. Keymap support is done
                    // with the `xkb` extension and the `xkbcommon-rs` crate)

                    // We toggle maximized state, for this we send a message
                    // by building a `x::ClientMessageEvent` with the proper
                    // atoms and send it to the server.

                    let data = x::ClientMessageData::Data32([
                        if maximized { 0 } else { 1 },
                        wm_state_maxv.resource_id(),
                        wm_state_maxh.resource_id(),
                        0,
                        0,
                    ]);
                    let event = x::ClientMessageEvent::new(window, wm_state, data);
                    let cookie = conn.send_request_checked(&x::SendEvent {
                        propagate: false,
                        destination: x::SendEventDest::Window(screen.root()),
                        event_mask: x::EventMask::STRUCTURE_NOTIFY,
                        event: &event,
                    });
                    conn.check_request(cookie)?;

                    // Same as before, if we don't check for error, we have to flush
                    // the connection.
                    // conn.flush()?;

                    maximized = !maximized;
                } else if ev.detail() == 0x18 {
                    // Q (on qwerty)

                    // We exit the event loop (and the program)
                    break Ok(());
                }
            }
            xcb::Event::X(x::Event::ClientMessage(ev)) => {
                // We have received a message from the server
                if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
                    if atom == wm_del_window.resource_id() {
                        // The received atom is "WM_DELETE_WINDOW".
                        // We can check here if the user needs to save before
                        // exit, or in our case, exit right away.
                        break Ok(());
                    }
                }
            }
            _ => {}
        }
    }
}

Cargo features

The following Cargo features are available

xlib_xcb

This feature activates the use of xlib::Display to connect to XCB, therefore making available both Xlib and XCB functions to the same connection. While XCB is sufficient to handle all communication with the X server, some things can still only be done by Xlib. E.g. hardware initialization for OpenGL is done by Xlib only.

debug_atom_names

When this feature is activated, the fmt::Debug implementation for x::Atom will print out the name of the atom in addition to its value.

E.g. the feature would turn Atom { res_id: 303 } into Atom("Abs Pressure" ; 303).

This can be useful in situations where you are not sure which atom you have to intern in order to look up some data in a reply.

It should be noted that the feature sets global variable to have access to the connection in the fmt::Debug implementation, and that the Debug print have side effects (x::GetAtomName requests) which can sometimes not be desirable. The feature should therefore only be activated when needed.

Extension features

The following X extensions are activated by a cargo feature:

Extension nameCargo feature
Compositecomposite
DAMAGEdamage
DPMSdpms
DRI2dri2
DRI3dri3
Generic Event Extensionge
GLXglx
Presentpresent
RANDRrandr
RECORDrecord
RENDERrender
X-Resourceres
MIT-SCREEN-SAVERscreensaver
SHAPEshape
MIT-SHMshm
SYNCsync
XEVIExevie
XFree86-DRIxf86dri
XFree86-VidModeExtensionxf86vidmode
XFIXESxfixes
XINERAMAxinerama
XInputExtensionxinput
XKEYBOARDxkb
XpExtensionxprint
SELinuxxselinux
TESTxtest
XVideoxv
XVideo-MotionCompensationxvmc

Modules

The BIG-REQUESTS extension.

The Composite X extension.

The DAMAGE X extension.

The DPMS X extension.

The DRI2 X extension.

The DRI3 X extension.

Module for Foreign Function Interface bindings.

The Generic Event Extension X extension.

The GLX X extension.

The Present X extension.

The RANDR X extension.

The RECORD X extension.

The RENDER X extension.

The X-Resource X extension.

The MIT-SCREEN-SAVER X extension.

The SHAPE X extension.

The MIT-SHM X extension.

The SYNC X extension.

The core X protocol definitions

The XC-MISC extension.

The XEVIE X extension.

The XFree86-DRI X extension.

The XFree86-VidModeExtension X extension.

The XFIXES X extension.

The XINERAMA X extension.

The XInputExtension X extension.

The XKEYBOARD X extension.

The XpExtension X extension.

The SELinux X extension.

The XTEST X extension.

The XVideo X extension.

The XVideo-MotionCompensation X extension.

Macros

An helper macro that generate a struct of atoms.

Structs

Container for authentication information to connect to the X server

Connection is the central object of XCB.

Display info returned by parse_display

Extension data as returned by each extensions get_extension_data.

A slice to a Latin-1 (aka. ISO 8859-1) string.

Latin-1 (aka. ISO 8859-1) of fixed size

A struct owning a Latin-1 (aka. ISO 8859-1) string.

A struct that serve as an identifier for internal special queue in XCB

an event was not recognized as part of the core protocol or any enabled extension

The default cookie type returned by void-requests.

The checked cookie type returned by void-requests.

Enums

Error type that is returned by Connection::has_error.

The general error type for Rust-XCB.

Unified Event type from the X server.

Determines whether Xlib or XCB owns the event queue of Connection.

Refers to a X protocol extension.

Error that can produce Latin-1 string operations

A protocol error issued from the X server

Traits

A trait to designate base protocol errors.

Trait for base events (aka. non GE_GENERIC events)

General trait for cookies returned by requests.

A marker trait for a cookie that allows synchronized error checking.

A trait for checked cookies of requests that send a reply.

A trait for unchecked cookies of requests that send a reply.

A trait for GE_GENERIC events

Trait for types that own a C allocated pointer and are represented by the data pointed to.

Trait implemented by all requests to send the serialized data over the wire.

Trait for request replies

Trait implemented by requests types.

Trait for requests that return a reply.

Marker trait for requests that do not return a reply.

A X resource trait

Trait for X resources that can be created directly from Connection::generate_id

Functions

Parses a display string in the form documented by X (7x).

Type Definitions

The result type associated with ConnError.

The result type associated with ProtocolError.

The general result type for Rust-XCB.