gpui: Switch to x11rb (#9113)

Switch to using `x11rb` crate instead of current `xcb` crate for gpui's
x11 platform.

Also fixes the crash on resize, and white flashing on resize.

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
Rajesh Malviya 2024-03-13 10:17:08 +05:30 committed by GitHub
parent 47afc70979
commit 80b80dfa78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 288 additions and 276 deletions

View file

@ -9,10 +9,11 @@ use crate::{
use blade_graphics as gpu;
use parking_lot::Mutex;
use raw_window_handle as rwh;
use xcb::{
x::{self, StackMode},
Xid as _,
use x11rb::{
connection::Connection,
protocol::xproto::{self, ConnectionExt as _, CreateWindowAux},
wrapper::ConnectionExt,
xcb_ffi::XCBConnection,
};
use std::{
@ -40,14 +41,13 @@ struct Callbacks {
appearance_changed: Option<Box<dyn FnMut()>>,
}
xcb::atoms_struct! {
#[derive(Debug)]
pub(crate) struct XcbAtoms {
pub wm_protocols => b"WM_PROTOCOLS",
pub wm_del_window => b"WM_DELETE_WINDOW",
wm_state => b"_NET_WM_STATE",
wm_state_maxv => b"_NET_WM_STATE_MAXIMIZED_VERT",
wm_state_maxh => b"_NET_WM_STATE_MAXIMIZED_HORZ",
x11rb::atom_manager! {
pub XcbAtoms: AtomsCookie {
WM_PROTOCOLS,
WM_DELETE_WINDOW,
_NET_WM_STATE,
_NET_WM_STATE_MAXIMIZED_VERT,
_NET_WM_STATE_MAXIMIZED_HORZ,
}
}
@ -68,30 +68,31 @@ impl LinuxWindowInner {
}
}
fn query_render_extent(xcb_connection: &xcb::Connection, x_window: x::Window) -> gpu::Extent {
let cookie = xcb_connection.send_request(&x::GetGeometry {
drawable: x::Drawable::Window(x_window),
});
let reply = xcb_connection.wait_for_reply(cookie).unwrap();
fn query_render_extent(xcb_connection: &XCBConnection, x_window: xproto::Window) -> gpu::Extent {
let reply = xcb_connection
.get_geometry(x_window)
.unwrap()
.reply()
.unwrap();
gpu::Extent {
width: reply.width() as u32,
height: reply.height() as u32,
width: reply.width as u32,
height: reply.height as u32,
depth: 1,
}
}
struct RawWindow {
connection: *mut c_void,
screen_id: i32,
screen_id: usize,
window_id: u32,
visual_id: u32,
}
pub(crate) struct X11WindowState {
xcb_connection: Rc<xcb::Connection>,
xcb_connection: Rc<XCBConnection>,
display: Rc<dyn PlatformDisplay>,
raw: RawWindow,
x_window: x::Window,
x_window: xproto::Window,
callbacks: RefCell<Callbacks>,
inner: RefCell<LinuxWindowInner>,
}
@ -112,7 +113,7 @@ unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
let mut dh = blade_rwh::XcbDisplayHandle::empty();
dh.connection = self.connection;
dh.screen = self.screen_id;
dh.screen = self.screen_id as i32;
dh.into()
}
}
@ -130,7 +131,7 @@ impl rwh::HasDisplayHandle for X11Window {
fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
Ok(unsafe {
let non_zero = NonNull::new(self.0.raw.connection).unwrap();
let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.0.raw.screen_id);
let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.0.raw.screen_id as i32);
rwh::DisplayHandle::borrow_raw(handle.into())
})
}
@ -139,76 +140,76 @@ impl rwh::HasDisplayHandle for X11Window {
impl X11WindowState {
pub fn new(
params: WindowParams,
xcb_connection: &Rc<xcb::Connection>,
x_main_screen_index: i32,
x_window: x::Window,
xcb_connection: &Rc<XCBConnection>,
x_main_screen_index: usize,
x_window: xproto::Window,
atoms: &XcbAtoms,
) -> Self {
let x_screen_index = params
.display_id
.map_or(x_main_screen_index, |did| did.0 as i32);
let screen = xcb_connection
.get_setup()
.roots()
.nth(x_screen_index as usize)
.map_or(x_main_screen_index, |did| did.0 as usize);
let screen = xcb_connection.setup().roots.get(x_screen_index).unwrap();
let win_aux = xproto::CreateWindowAux::new().event_mask(
xproto::EventMask::EXPOSURE
| xproto::EventMask::STRUCTURE_NOTIFY
| xproto::EventMask::ENTER_WINDOW
| xproto::EventMask::LEAVE_WINDOW
| xproto::EventMask::FOCUS_CHANGE
| xproto::EventMask::KEY_PRESS
| xproto::EventMask::KEY_RELEASE
| xproto::EventMask::BUTTON_PRESS
| xproto::EventMask::BUTTON_RELEASE
| xproto::EventMask::POINTER_MOTION
| xproto::EventMask::BUTTON1_MOTION
| xproto::EventMask::BUTTON2_MOTION
| xproto::EventMask::BUTTON3_MOTION
| xproto::EventMask::BUTTON4_MOTION
| xproto::EventMask::BUTTON5_MOTION
| xproto::EventMask::BUTTON_MOTION,
);
xcb_connection
.create_window(
x11rb::COPY_FROM_PARENT as _,
x_window,
screen.root,
params.bounds.origin.x.0 as i16,
params.bounds.origin.y.0 as i16,
params.bounds.size.width.0 as u16,
params.bounds.size.height.0 as u16,
0,
xproto::WindowClass::INPUT_OUTPUT,
screen.root_visual,
&win_aux,
)
.unwrap();
let xcb_values = [
x::Cw::BackPixel(screen.white_pixel()),
x::Cw::EventMask(
x::EventMask::EXPOSURE
| x::EventMask::STRUCTURE_NOTIFY
| x::EventMask::ENTER_WINDOW
| x::EventMask::LEAVE_WINDOW
| x::EventMask::FOCUS_CHANGE
| x::EventMask::KEY_PRESS
| x::EventMask::KEY_RELEASE
| x::EventMask::BUTTON_PRESS
| x::EventMask::BUTTON_RELEASE
| x::EventMask::POINTER_MOTION
| x::EventMask::BUTTON1_MOTION
| x::EventMask::BUTTON2_MOTION
| x::EventMask::BUTTON3_MOTION
| x::EventMask::BUTTON4_MOTION
| x::EventMask::BUTTON5_MOTION
| x::EventMask::BUTTON_MOTION,
),
];
xcb_connection.send_request(&x::CreateWindow {
depth: x::COPY_FROM_PARENT as u8,
wid: x_window,
parent: screen.root(),
x: params.bounds.origin.x.0 as i16,
y: params.bounds.origin.y.0 as i16,
width: params.bounds.size.width.0 as u16,
height: params.bounds.size.height.0 as u16,
border_width: 0,
class: x::WindowClass::InputOutput,
visual: screen.root_visual(),
value_list: &xcb_values,
});
if let Some(titlebar) = params.titlebar {
if let Some(title) = titlebar.title {
xcb_connection.send_request(&x::ChangeProperty {
mode: x::PropMode::Replace,
window: x_window,
property: x::ATOM_WM_NAME,
r#type: x::ATOM_STRING,
data: title.as_bytes(),
});
xcb_connection
.change_property8(
xproto::PropMode::REPLACE,
x_window,
xproto::AtomEnum::WM_NAME,
xproto::AtomEnum::STRING,
title.as_bytes(),
)
.unwrap();
}
}
xcb_connection.send_request(&x::ChangeProperty {
mode: x::PropMode::Replace,
window: x_window,
property: atoms.wm_protocols,
r#type: x::ATOM_ATOM,
data: &[atoms.wm_del_window],
});
xcb_connection.send_request(&x::MapWindow { window: x_window });
xcb_connection
.change_property32(
xproto::PropMode::REPLACE,
x_window,
atoms.WM_PROTOCOLS,
xproto::AtomEnum::ATOM,
&[atoms.WM_DELETE_WINDOW],
)
.unwrap();
xcb_connection.map_window(x_window).unwrap();
xcb_connection.flush().unwrap();
let raw = RawWindow {
@ -216,8 +217,8 @@ impl X11WindowState {
xcb_connection,
) as *mut _,
screen_id: x_screen_index,
window_id: x_window.resource_id(),
visual_id: screen.root_visual(),
window_id: x_window,
visual_id: screen.root_visual,
};
let gpu = Arc::new(
unsafe {
@ -254,12 +255,8 @@ impl X11WindowState {
pub fn destroy(&self) {
self.inner.borrow_mut().renderer.destroy();
self.xcb_connection.send_request(&x::UnmapWindow {
window: self.x_window,
});
self.xcb_connection.send_request(&x::DestroyWindow {
window: self.x_window,
});
self.xcb_connection.unmap_window(self.x_window).unwrap();
self.xcb_connection.destroy_window(self.x_window).unwrap();
if let Some(fun) = self.callbacks.borrow_mut().close.take() {
fun();
}
@ -359,14 +356,14 @@ impl PlatformWindow for X11Window {
}
fn mouse_position(&self) -> Point<Pixels> {
let cookie = self.0.xcb_connection.send_request(&x::QueryPointer {
window: self.0.x_window,
});
let reply: x::QueryPointerReply = self.0.xcb_connection.wait_for_reply(cookie).unwrap();
Point::new(
(reply.root_x() as u32).into(),
(reply.root_y() as u32).into(),
)
let reply = self
.0
.xcb_connection
.query_pointer(self.0.x_window)
.unwrap()
.reply()
.unwrap();
Point::new((reply.root_x as u32).into(), (reply.root_y as u32).into())
}
// todo(linux)
@ -397,20 +394,24 @@ impl PlatformWindow for X11Window {
}
fn activate(&self) {
self.0.xcb_connection.send_request(&x::ConfigureWindow {
window: self.0.x_window,
value_list: &[x::ConfigWindow::StackMode(x::StackMode::Above)],
});
let win_aux = xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE);
self.0
.xcb_connection
.configure_window(self.0.x_window, &win_aux)
.unwrap();
}
fn set_title(&mut self, title: &str) {
self.0.xcb_connection.send_request(&x::ChangeProperty {
mode: x::PropMode::Replace,
window: self.0.x_window,
property: x::ATOM_WM_NAME,
r#type: x::ATOM_STRING,
data: title.as_bytes(),
});
self.0
.xcb_connection
.change_property8(
xproto::PropMode::REPLACE,
self.0.x_window,
xproto::AtomEnum::WM_NAME,
xproto::AtomEnum::STRING,
title.as_bytes(),
)
.unwrap();
}
// todo(linux)
@ -443,7 +444,7 @@ impl PlatformWindow for X11Window {
// todo(linux)
fn is_full_screen(&self) -> bool {
unimplemented!()
false
}
fn on_request_frame(&self, callback: Box<dyn FnMut()>) {