Skip to content

Commit 3eeafad

Browse files
authored
x11: Add XCB support to the X11 backend (#52)
1 parent d5bb2c1 commit 3eeafad

File tree

4 files changed

+295
-80
lines changed

4 files changed

+295
-80
lines changed

Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,21 @@ exclude = ["examples"]
1515
default = ["x11", "wayland", "wayland-dlopen"]
1616
wayland = ["wayland-backend", "wayland-client", "nix"]
1717
wayland-dlopen = ["wayland-sys/dlopen"]
18-
x11 = ["x11-dl"]
18+
x11 = ["bytemuck", "x11rb", "x11-dl"]
1919

2020
[dependencies]
2121
thiserror = "1.0.30"
2222
raw-window-handle = "0.5.0"
23+
log = "0.4.17"
2324

2425
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
2526
nix = { version = "0.26.1", optional = true }
2627
wayland-backend = { version = "0.1.0-beta.14", features = ["client_system"], optional = true }
2728
wayland-client = { version = "0.30.0-beta.14", optional = true }
2829
wayland-sys = "0.30.0"
30+
bytemuck = { version = "1.12.3", optional = true }
2931
x11-dl = { version = "2.19.1", optional = true }
32+
x11rb = { version = "0.11.0", features = ["allow-unsafe-code", "dl-libxcb"], optional = true }
3033

3134
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
3235
version = "0.42.0"

examples/libxcb.rs

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//! Example of using `softbuffer` with `libxcb`.
2+
3+
#[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))]
4+
mod example {
5+
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
6+
use softbuffer::GraphicsContext;
7+
use x11rb::{
8+
connection::Connection,
9+
protocol::{
10+
xproto::{self, ConnectionExt as _},
11+
Event,
12+
},
13+
xcb_ffi::XCBConnection,
14+
};
15+
16+
pub(crate) fn run() {
17+
// Create a new XCB connection
18+
let (conn, screen) = XCBConnection::connect(None).expect("Failed to connect to X server");
19+
20+
// x11rb doesn't use raw-window-handle yet, so just create our own.
21+
let mut display_handle = XcbDisplayHandle::empty();
22+
display_handle.connection = conn.get_raw_xcb_connection() as *mut _;
23+
display_handle.screen = screen as _;
24+
25+
// Create a new window.
26+
let mut width = 640u16;
27+
let mut height = 480u16;
28+
29+
let window = conn.generate_id().unwrap();
30+
let screen = &conn.setup().roots[screen];
31+
let (root_visual, root_parent) = (screen.root_visual, screen.root);
32+
conn.create_window(
33+
x11rb::COPY_FROM_PARENT as _,
34+
window,
35+
root_parent,
36+
0,
37+
0,
38+
width,
39+
height,
40+
0,
41+
xproto::WindowClass::COPY_FROM_PARENT,
42+
root_visual,
43+
&xproto::CreateWindowAux::new()
44+
.background_pixel(screen.white_pixel)
45+
.event_mask(xproto::EventMask::EXPOSURE | xproto::EventMask::STRUCTURE_NOTIFY),
46+
)
47+
.unwrap()
48+
.check()
49+
.unwrap();
50+
51+
let mut window_handle = XcbWindowHandle::empty();
52+
window_handle.window = window as _;
53+
window_handle.visual_id = root_visual as _;
54+
55+
// Create a new softbuffer context.
56+
// SAFETY: The display and window handles outlive the context.
57+
let mut context = unsafe {
58+
GraphicsContext::from_raw(
59+
RawWindowHandle::Xcb(window_handle),
60+
RawDisplayHandle::Xcb(display_handle),
61+
)
62+
}
63+
.unwrap();
64+
65+
// Register an atom for closing the window.
66+
let wm_protocols_atom = conn
67+
.intern_atom(false, "WM_PROTOCOLS".as_bytes())
68+
.unwrap()
69+
.reply()
70+
.unwrap()
71+
.atom;
72+
let delete_window_atom = conn
73+
.intern_atom(false, "WM_DELETE_WINDOW".as_bytes())
74+
.unwrap()
75+
.reply()
76+
.unwrap()
77+
.atom;
78+
conn.change_property(
79+
xproto::PropMode::REPLACE as _,
80+
window,
81+
wm_protocols_atom,
82+
xproto::AtomEnum::ATOM,
83+
32,
84+
1,
85+
&delete_window_atom.to_ne_bytes(),
86+
)
87+
.unwrap()
88+
.check()
89+
.unwrap();
90+
91+
// Map the window to the screen.
92+
conn.map_window(window).unwrap().check().unwrap();
93+
94+
// Pump events.
95+
loop {
96+
let event = conn.wait_for_event().unwrap();
97+
98+
match event {
99+
Event::Expose(_) => {
100+
// Draw a width x height red rectangle.
101+
let red = 255 << 16;
102+
let source = std::iter::repeat(red)
103+
.take((width as usize * height as usize) as _)
104+
.collect::<Vec<_>>();
105+
106+
// Draw the buffer.
107+
context.set_buffer(&source, width, height);
108+
}
109+
Event::ConfigureNotify(configure_notify) => {
110+
width = configure_notify.width;
111+
height = configure_notify.height;
112+
}
113+
Event::ClientMessage(cm) => {
114+
if cm.data.as_data32()[0] == delete_window_atom {
115+
break;
116+
}
117+
}
118+
_ => {}
119+
}
120+
}
121+
122+
// Delete the context and drop the window.
123+
drop(context);
124+
conn.destroy_window(window).unwrap().check().unwrap();
125+
}
126+
}
127+
128+
#[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))]
129+
fn main() {
130+
example::run();
131+
}
132+
133+
#[cfg(not(all(feature = "x11", any(target_os = "linux", target_os = "freebsd"))))]
134+
fn main() {
135+
eprintln!("This example requires the `x11` feature to be enabled on a supported platform.");
136+
}

src/lib.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,14 @@ impl GraphicsContext {
110110
RawWindowHandle::Xlib(xlib_window_handle),
111111
RawDisplayHandle::Xlib(xlib_display_handle),
112112
) => Dispatch::X11(unsafe {
113-
x11::X11Impl::new(xlib_window_handle, xlib_display_handle)?
113+
x11::X11Impl::from_xlib(xlib_window_handle, xlib_display_handle)?
114+
}),
115+
#[cfg(all(feature = "x11", any(target_os = "linux", target_os = "freebsd")))]
116+
(
117+
RawWindowHandle::Xcb(xcb_window_handle),
118+
RawDisplayHandle::Xcb(xcb_display_handle),
119+
) => Dispatch::X11(unsafe {
120+
x11::X11Impl::from_xcb(xcb_window_handle, xcb_display_handle)?
114121
}),
115122
#[cfg(all(feature = "wayland", any(target_os = "linux", target_os = "freebsd")))]
116123
(

0 commit comments

Comments
 (0)