Skip to content

Commit fdc5d28

Browse files
authored
Switch from xcb crate to x11rb (#173)
Replace the `xcb` and `xcb-util` crates with `x11rb`. We were using an old version of the `xcb` crate which had some soundness issue. `x11rb` doesn't have these issues and generally provides a safer and nicer to use API. It's possible to use `x11rb` without linking to xcb at all, using the `RustConnection` API, but unfortunately we have to use the `XCBConnection` API (which uses xcb under the hood) due to our use of the xlib GLX API for creating OpenGL contexts. In the future, it might be possible to avoid linking to xlib and xcb by replacing GLX with EGL. Getting the xlib-xcb integration to work also necessitated upgrading the version of the `x11` crate, since the version we were using was missing some necessary functionality that was previously being provided by the `xcb` crate.
1 parent 998ced8 commit fdc5d28

File tree

5 files changed

+323
-399
lines changed

5 files changed

+323
-399
lines changed

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ keyboard-types = { version = "0.6.1", default-features = false }
2323
raw-window-handle = "0.5"
2424

2525
[target.'cfg(target_os="linux")'.dependencies]
26-
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] }
27-
x11 = { version = "2.18", features = ["xlib", "xcursor"] }
28-
xcb-util = { version = "0.3", features = ["icccm"] }
26+
x11rb = { version = "0.13.0", features = ["cursor", "resource_manager", "allow-unsafe-code"] }
27+
x11 = { version = "2.21", features = ["xlib", "xcursor", "xlib_xcb"] }
2928
nix = "0.22.0"
3029

3130
[target.'cfg(target_os="windows")'.dependencies]

src/x11/cursor.rs

Lines changed: 81 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,100 @@
1-
use std::os::raw::c_char;
1+
use std::error::Error;
2+
3+
use x11rb::connection::Connection;
4+
use x11rb::cursor::Handle as CursorHandle;
5+
use x11rb::protocol::xproto::{ConnectionExt as _, Cursor};
6+
use x11rb::xcb_ffi::XCBConnection;
27

38
use crate::MouseCursor;
49

5-
fn create_empty_cursor(display: *mut x11::xlib::Display) -> Option<u32> {
6-
let data = 0;
7-
let pixmap = unsafe {
8-
let screen = x11::xlib::XDefaultScreen(display);
9-
let window = x11::xlib::XRootWindow(display, screen);
10-
x11::xlib::XCreateBitmapFromData(display, window, &data, 1, 1)
11-
};
10+
fn create_empty_cursor(conn: &XCBConnection, screen: usize) -> Result<Cursor, Box<dyn Error>> {
11+
let cursor_id = conn.generate_id()?;
12+
let pixmap_id = conn.generate_id()?;
13+
let root_window = conn.setup().roots[screen].root;
14+
conn.create_pixmap(1, pixmap_id, root_window, 1, 1)?;
15+
conn.create_cursor(cursor_id, pixmap_id, pixmap_id, 0, 0, 0, 0, 0, 0, 0, 0)?;
16+
conn.free_pixmap(pixmap_id)?;
1217

13-
if pixmap == 0 {
14-
return None;
15-
}
16-
17-
unsafe {
18-
// We don't care about this color, since it only fills bytes
19-
// in the pixmap which are not 0 in the mask.
20-
let mut color: x11::xlib::XColor = std::mem::zeroed();
21-
22-
let cursor = x11::xlib::XCreatePixmapCursor(
23-
display,
24-
pixmap,
25-
pixmap,
26-
&mut color as *mut _,
27-
&mut color as *mut _,
28-
0,
29-
0,
30-
);
31-
x11::xlib::XFreePixmap(display, pixmap);
32-
33-
Some(cursor as u32)
34-
}
18+
Ok(cursor_id)
3519
}
3620

37-
fn load_cursor(display: *mut x11::xlib::Display, name: &[u8]) -> Option<u32> {
38-
let xcursor =
39-
unsafe { x11::xcursor::XcursorLibraryLoadCursor(display, name.as_ptr() as *const c_char) };
40-
41-
if xcursor == 0 {
42-
None
21+
fn load_cursor(
22+
conn: &XCBConnection, cursor_handle: &CursorHandle, name: &str,
23+
) -> Result<Option<Cursor>, Box<dyn Error>> {
24+
let cursor = cursor_handle.load_cursor(conn, name)?;
25+
if cursor != x11rb::NONE {
26+
Ok(Some(cursor))
4327
} else {
44-
Some(xcursor as u32)
28+
Ok(None)
4529
}
4630
}
4731

48-
fn load_first_existing_cursor(display: *mut x11::xlib::Display, names: &[&[u8]]) -> Option<u32> {
49-
names
50-
.iter()
51-
.map(|name| load_cursor(display, name))
52-
.find(|xcursor| xcursor.is_some())
53-
.unwrap_or(None)
32+
fn load_first_existing_cursor(
33+
conn: &XCBConnection, cursor_handle: &CursorHandle, names: &[&str],
34+
) -> Result<Option<Cursor>, Box<dyn Error>> {
35+
for name in names {
36+
let cursor = load_cursor(conn, cursor_handle, name)?;
37+
if cursor.is_some() {
38+
return Ok(cursor);
39+
}
40+
}
41+
42+
Ok(None)
5443
}
5544

56-
pub(super) fn get_xcursor(display: *mut x11::xlib::Display, cursor: MouseCursor) -> u32 {
57-
let load = |name: &[u8]| load_cursor(display, name);
58-
let loadn = |names: &[&[u8]]| load_first_existing_cursor(display, names);
45+
pub(super) fn get_xcursor(
46+
conn: &XCBConnection, screen: usize, cursor_handle: &CursorHandle, cursor: MouseCursor,
47+
) -> Result<Cursor, Box<dyn Error>> {
48+
let load = |name: &str| load_cursor(conn, cursor_handle, name);
49+
let loadn = |names: &[&str]| load_first_existing_cursor(conn, cursor_handle, names);
5950

6051
let cursor = match cursor {
6152
MouseCursor::Default => None, // catch this in the fallback case below
6253

63-
MouseCursor::Hand => loadn(&[b"hand2\0", b"hand1\0"]),
64-
MouseCursor::HandGrabbing => loadn(&[b"closedhand\0", b"grabbing\0"]),
65-
MouseCursor::Help => load(b"question_arrow\0"),
66-
67-
MouseCursor::Hidden => create_empty_cursor(display),
68-
69-
MouseCursor::Text => loadn(&[b"text\0", b"xterm\0"]),
70-
MouseCursor::VerticalText => load(b"vertical-text\0"),
71-
72-
MouseCursor::Working => load(b"watch\0"),
73-
MouseCursor::PtrWorking => load(b"left_ptr_watch\0"),
74-
75-
MouseCursor::NotAllowed => load(b"crossed_circle\0"),
76-
MouseCursor::PtrNotAllowed => loadn(&[b"no-drop\0", b"crossed_circle\0"]),
77-
78-
MouseCursor::ZoomIn => load(b"zoom-in\0"),
79-
MouseCursor::ZoomOut => load(b"zoom-out\0"),
80-
81-
MouseCursor::Alias => load(b"link\0"),
82-
MouseCursor::Copy => load(b"copy\0"),
83-
MouseCursor::Move => load(b"move\0"),
84-
MouseCursor::AllScroll => load(b"all-scroll\0"),
85-
MouseCursor::Cell => load(b"plus\0"),
86-
MouseCursor::Crosshair => load(b"crosshair\0"),
87-
88-
MouseCursor::EResize => load(b"right_side\0"),
89-
MouseCursor::NResize => load(b"top_side\0"),
90-
MouseCursor::NeResize => load(b"top_right_corner\0"),
91-
MouseCursor::NwResize => load(b"top_left_corner\0"),
92-
MouseCursor::SResize => load(b"bottom_side\0"),
93-
MouseCursor::SeResize => load(b"bottom_right_corner\0"),
94-
MouseCursor::SwResize => load(b"bottom_left_corner\0"),
95-
MouseCursor::WResize => load(b"left_side\0"),
96-
MouseCursor::EwResize => load(b"h_double_arrow\0"),
97-
MouseCursor::NsResize => load(b"v_double_arrow\0"),
98-
MouseCursor::NwseResize => loadn(&[b"bd_double_arrow\0", b"size_bdiag\0"]),
99-
MouseCursor::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_fdiag\0"]),
100-
MouseCursor::ColResize => loadn(&[b"split_h\0", b"h_double_arrow\0"]),
101-
MouseCursor::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]),
54+
MouseCursor::Hand => loadn(&["hand2", "hand1"])?,
55+
MouseCursor::HandGrabbing => loadn(&["closedhand", "grabbing"])?,
56+
MouseCursor::Help => load("question_arrow")?,
57+
58+
MouseCursor::Hidden => Some(create_empty_cursor(conn, screen)?),
59+
60+
MouseCursor::Text => loadn(&["text", "xterm"])?,
61+
MouseCursor::VerticalText => load("vertical-text")?,
62+
63+
MouseCursor::Working => load("watch")?,
64+
MouseCursor::PtrWorking => load("left_ptr_watch")?,
65+
66+
MouseCursor::NotAllowed => load("crossed_circle")?,
67+
MouseCursor::PtrNotAllowed => loadn(&["no-drop", "crossed_circle"])?,
68+
69+
MouseCursor::ZoomIn => load("zoom-in")?,
70+
MouseCursor::ZoomOut => load("zoom-out")?,
71+
72+
MouseCursor::Alias => load("link")?,
73+
MouseCursor::Copy => load("copy")?,
74+
MouseCursor::Move => load("move")?,
75+
MouseCursor::AllScroll => load("all-scroll")?,
76+
MouseCursor::Cell => load("plus")?,
77+
MouseCursor::Crosshair => load("crosshair")?,
78+
79+
MouseCursor::EResize => load("right_side")?,
80+
MouseCursor::NResize => load("top_side")?,
81+
MouseCursor::NeResize => load("top_right_corner")?,
82+
MouseCursor::NwResize => load("top_left_corner")?,
83+
MouseCursor::SResize => load("bottom_side")?,
84+
MouseCursor::SeResize => load("bottom_right_corner")?,
85+
MouseCursor::SwResize => load("bottom_left_corner")?,
86+
MouseCursor::WResize => load("left_side")?,
87+
MouseCursor::EwResize => load("h_double_arrow")?,
88+
MouseCursor::NsResize => load("v_double_arrow")?,
89+
MouseCursor::NwseResize => loadn(&["bd_double_arrow", "size_bdiag"])?,
90+
MouseCursor::NeswResize => loadn(&["fd_double_arrow", "size_fdiag"])?,
91+
MouseCursor::ColResize => loadn(&["split_h", "h_double_arrow"])?,
92+
MouseCursor::RowResize => loadn(&["split_v", "v_double_arrow"])?,
10293
};
10394

104-
cursor.or_else(|| load(b"left_ptr\0")).unwrap_or(0)
95+
if let Some(cursor) = cursor {
96+
Ok(cursor)
97+
} else {
98+
Ok(load("left_ptr")?.unwrap_or(x11rb::NONE))
99+
}
105100
}

src/x11/keyboard.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
//! X11 keyboard handling
2020
21-
use xcb::xproto;
21+
use x11rb::protocol::xproto::{KeyButMask, KeyPressEvent, KeyReleaseEvent};
2222

2323
use keyboard_types::*;
2424

@@ -361,43 +361,43 @@ fn hardware_keycode_to_code(hw_keycode: u16) -> Code {
361361
}
362362

363363
// Extracts the keyboard modifiers from, e.g., the `state` field of
364-
// `xcb::xproto::ButtonPressEvent`
365-
pub(super) fn key_mods(mods: u16) -> Modifiers {
364+
// `x11rb::protocol::xproto::ButtonPressEvent`
365+
pub(super) fn key_mods(mods: KeyButMask) -> Modifiers {
366366
let mut ret = Modifiers::default();
367-
let mut key_masks = [
368-
(xproto::MOD_MASK_SHIFT, Modifiers::SHIFT),
369-
(xproto::MOD_MASK_CONTROL, Modifiers::CONTROL),
367+
let key_masks = [
368+
(KeyButMask::SHIFT, Modifiers::SHIFT),
369+
(KeyButMask::CONTROL, Modifiers::CONTROL),
370370
// X11's mod keys are configurable, but this seems
371371
// like a reasonable default for US keyboards, at least,
372372
// where the "windows" key seems to be MOD_MASK_4.
373-
(xproto::MOD_MASK_1, Modifiers::ALT),
374-
(xproto::MOD_MASK_2, Modifiers::NUM_LOCK),
375-
(xproto::MOD_MASK_4, Modifiers::META),
376-
(xproto::MOD_MASK_LOCK, Modifiers::CAPS_LOCK),
373+
(KeyButMask::BUTTON1, Modifiers::ALT),
374+
(KeyButMask::BUTTON2, Modifiers::NUM_LOCK),
375+
(KeyButMask::BUTTON4, Modifiers::META),
376+
(KeyButMask::LOCK, Modifiers::CAPS_LOCK),
377377
];
378-
for (mask, modifiers) in &mut key_masks {
379-
if mods & (*mask as u16) != 0 {
378+
for (mask, modifiers) in &key_masks {
379+
if mods.contains(*mask) {
380380
ret |= *modifiers;
381381
}
382382
}
383383
ret
384384
}
385385

386-
pub(super) fn convert_key_press_event(key_press: &xcb::KeyPressEvent) -> KeyboardEvent {
387-
let hw_keycode = key_press.detail();
386+
pub(super) fn convert_key_press_event(key_press: &KeyPressEvent) -> KeyboardEvent {
387+
let hw_keycode = key_press.detail;
388388
let code = hardware_keycode_to_code(hw_keycode.into());
389-
let modifiers = key_mods(key_press.state());
389+
let modifiers = key_mods(key_press.state);
390390
let key = code_to_key(code, modifiers);
391391
let location = code_to_location(code);
392392
let state = KeyState::Down;
393393

394394
KeyboardEvent { code, key, modifiers, location, state, repeat: false, is_composing: false }
395395
}
396396

397-
pub(super) fn convert_key_release_event(key_release: &xcb::KeyReleaseEvent) -> KeyboardEvent {
398-
let hw_keycode = key_release.detail();
397+
pub(super) fn convert_key_release_event(key_release: &KeyReleaseEvent) -> KeyboardEvent {
398+
let hw_keycode = key_release.detail;
399399
let code = hardware_keycode_to_code(hw_keycode.into());
400-
let modifiers = key_mods(key_release.state());
400+
let modifiers = key_mods(key_release.state);
401401
let key = code_to_key(code, modifiers);
402402
let location = code_to_location(code);
403403
let state = KeyState::Up;

0 commit comments

Comments
 (0)