2021-01-04 08:18:35 +00:00
|
|
|
use std::{cell::RefCell, collections::HashMap, convert::TryFrom, os::unix::net::UnixStream, rc::Rc};
|
2021-01-04 07:17:16 +00:00
|
|
|
|
|
|
|
use smithay::{
|
2021-06-07 17:04:15 +00:00
|
|
|
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
2021-06-23 07:43:53 +00:00
|
|
|
wayland::compositor::give_role,
|
2021-01-04 07:17:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
use x11rb::{
|
|
|
|
connection::Connection as _,
|
|
|
|
errors::ReplyOrIdError,
|
|
|
|
protocol::{
|
|
|
|
composite::{ConnectionExt as _, Redirect},
|
|
|
|
xproto::{
|
|
|
|
ChangeWindowAttributesAux, ConfigWindow, ConfigureWindowAux, ConnectionExt as _, EventMask,
|
2021-01-04 08:18:35 +00:00
|
|
|
Window, WindowClass,
|
2021-01-04 07:17:16 +00:00
|
|
|
},
|
|
|
|
Event,
|
|
|
|
},
|
|
|
|
rust_connection::{DefaultStream, RustConnection},
|
|
|
|
};
|
|
|
|
|
2021-01-04 08:46:59 +00:00
|
|
|
use crate::{
|
2021-06-23 07:43:53 +00:00
|
|
|
window_map::{Kind, WindowMap},
|
2021-01-04 08:46:59 +00:00
|
|
|
AnvilState,
|
|
|
|
};
|
2021-01-04 07:17:16 +00:00
|
|
|
|
|
|
|
use x11rb_event_source::X11Source;
|
|
|
|
|
|
|
|
mod x11rb_event_source;
|
|
|
|
|
2021-06-07 17:04:15 +00:00
|
|
|
impl<BackendData: 'static> AnvilState<BackendData> {
|
|
|
|
pub fn start_xwayland(&mut self) {
|
|
|
|
if let Err(e) = self.xwayland.start() {
|
|
|
|
error!(self.log, "Failed to start XWayland: {}", e);
|
2021-01-04 08:46:59 +00:00
|
|
|
}
|
2021-01-04 07:17:16 +00:00
|
|
|
}
|
|
|
|
|
2021-06-07 17:04:15 +00:00
|
|
|
pub fn xwayland_ready(&mut self, connection: UnixStream, client: Client) {
|
2021-06-23 07:43:53 +00:00
|
|
|
let (wm, source) = X11State::start_wm(connection, self.window_map.clone(), self.log.clone()).unwrap();
|
2021-01-04 08:18:35 +00:00
|
|
|
let wm = Rc::new(RefCell::new(wm));
|
|
|
|
client.data_map().insert_if_missing(|| Rc::clone(&wm));
|
2021-01-04 07:17:16 +00:00
|
|
|
self.handle
|
|
|
|
.insert_source(source, move |events, _, _| {
|
2021-01-04 08:18:35 +00:00
|
|
|
let mut wm = wm.borrow_mut();
|
2021-01-04 07:17:16 +00:00
|
|
|
for event in events.into_iter() {
|
2021-01-04 08:18:35 +00:00
|
|
|
wm.handle_event(event, &client)?;
|
2021-01-04 07:17:16 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
2021-06-07 17:04:15 +00:00
|
|
|
pub fn xwayland_exited(&mut self) {
|
|
|
|
error!(self.log, "Xwayland crashed");
|
|
|
|
}
|
2021-01-04 07:17:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
x11rb::atom_manager! {
|
|
|
|
Atoms: AtomsCookie {
|
|
|
|
WM_S0,
|
|
|
|
WL_SURFACE_ID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The actual runtime state of the XWayland integration.
|
|
|
|
struct X11State {
|
|
|
|
conn: Rc<RustConnection>,
|
|
|
|
atoms: Atoms,
|
|
|
|
log: slog::Logger,
|
2021-01-04 09:07:35 +00:00
|
|
|
unpaired_surfaces: HashMap<u32, (Window, (i32, i32))>,
|
2021-06-23 07:43:53 +00:00
|
|
|
window_map: Rc<RefCell<WindowMap>>,
|
2021-01-04 07:17:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl X11State {
|
2021-01-04 08:46:59 +00:00
|
|
|
fn start_wm(
|
|
|
|
connection: UnixStream,
|
2021-06-23 07:43:53 +00:00
|
|
|
window_map: Rc<RefCell<WindowMap>>,
|
2021-01-04 08:46:59 +00:00
|
|
|
log: slog::Logger,
|
|
|
|
) -> Result<(Self, X11Source), Box<dyn std::error::Error>> {
|
2021-01-04 07:17:16 +00:00
|
|
|
// Create an X11 connection. XWayland only uses screen 0.
|
|
|
|
let screen = 0;
|
|
|
|
let stream = DefaultStream::from_unix_stream(connection)?;
|
|
|
|
let conn = RustConnection::connect_to_stream(stream, screen)?;
|
|
|
|
let atoms = Atoms::new(&conn)?.reply()?;
|
|
|
|
|
|
|
|
let screen = &conn.setup().roots[0];
|
|
|
|
|
|
|
|
// Actually become the WM by redirecting some operations
|
|
|
|
conn.change_window_attributes(
|
|
|
|
screen.root,
|
|
|
|
&ChangeWindowAttributesAux::default().event_mask(EventMask::SubstructureRedirect),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Tell XWayland that we are the WM by acquiring the WM_S0 selection. No X11 clients are accepted before this.
|
|
|
|
let win = conn.generate_id()?;
|
|
|
|
conn.create_window(
|
|
|
|
screen.root_depth,
|
|
|
|
win,
|
|
|
|
screen.root,
|
|
|
|
// x, y, width, height, border width
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
WindowClass::InputOutput,
|
|
|
|
x11rb::COPY_FROM_PARENT,
|
|
|
|
&Default::default(),
|
|
|
|
)?;
|
|
|
|
conn.set_selection_owner(win, atoms.WM_S0, x11rb::CURRENT_TIME)?;
|
|
|
|
|
|
|
|
// XWayland wants us to do this to function properly...?
|
|
|
|
conn.composite_redirect_subwindows(screen.root, Redirect::Manual)?;
|
|
|
|
|
|
|
|
conn.flush()?;
|
|
|
|
|
|
|
|
let conn = Rc::new(conn);
|
|
|
|
let wm = Self {
|
|
|
|
conn: Rc::clone(&conn),
|
|
|
|
atoms,
|
2021-01-04 08:18:35 +00:00
|
|
|
unpaired_surfaces: Default::default(),
|
2021-01-04 08:46:59 +00:00
|
|
|
window_map,
|
2021-01-04 07:17:16 +00:00
|
|
|
log,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok((wm, X11Source::new(conn)))
|
|
|
|
}
|
|
|
|
|
2021-01-04 08:18:35 +00:00
|
|
|
fn handle_event(&mut self, event: Event, client: &Client) -> Result<(), ReplyOrIdError> {
|
2021-01-04 07:17:16 +00:00
|
|
|
debug!(self.log, "X11: Got event {:?}", event);
|
|
|
|
match event {
|
|
|
|
Event::ConfigureRequest(r) => {
|
|
|
|
// Just grant the wish
|
|
|
|
let mut aux = ConfigureWindowAux::default();
|
|
|
|
if r.value_mask & u16::from(ConfigWindow::StackMode) != 0 {
|
|
|
|
aux = aux.stack_mode(r.stack_mode);
|
|
|
|
}
|
|
|
|
if r.value_mask & u16::from(ConfigWindow::Sibling) != 0 {
|
|
|
|
aux = aux.sibling(r.sibling);
|
|
|
|
}
|
|
|
|
if r.value_mask & u16::from(ConfigWindow::X) != 0 {
|
|
|
|
aux = aux.x(i32::try_from(r.x).unwrap());
|
|
|
|
}
|
|
|
|
if r.value_mask & u16::from(ConfigWindow::Y) != 0 {
|
|
|
|
aux = aux.y(i32::try_from(r.y).unwrap());
|
|
|
|
}
|
|
|
|
if r.value_mask & u16::from(ConfigWindow::Width) != 0 {
|
|
|
|
aux = aux.width(u32::try_from(r.width).unwrap());
|
|
|
|
}
|
|
|
|
if r.value_mask & u16::from(ConfigWindow::Height) != 0 {
|
|
|
|
aux = aux.height(u32::try_from(r.height).unwrap());
|
|
|
|
}
|
|
|
|
if r.value_mask & u16::from(ConfigWindow::BorderWidth) != 0 {
|
|
|
|
aux = aux.border_width(u32::try_from(r.border_width).unwrap());
|
|
|
|
}
|
|
|
|
self.conn.configure_window(r.window, &aux)?;
|
|
|
|
}
|
|
|
|
Event::MapRequest(r) => {
|
|
|
|
// Just grant the wish
|
|
|
|
self.conn.map_window(r.window)?;
|
|
|
|
}
|
|
|
|
Event::ClientMessage(msg) => {
|
|
|
|
if msg.type_ == self.atoms.WL_SURFACE_ID {
|
2021-01-04 08:18:35 +00:00
|
|
|
// We get a WL_SURFACE_ID message when Xwayland creates a WlSurface for a
|
|
|
|
// window. Both the creation of the surface and this client message happen at
|
|
|
|
// roughly the same time and are sent over different sockets (X11 socket and
|
|
|
|
// wayland socket). Thus, we could receive these two in any order. Hence, it
|
|
|
|
// can happen that we get None below when X11 was faster than Wayland.
|
|
|
|
|
2021-01-04 09:07:35 +00:00
|
|
|
let location = {
|
|
|
|
match self.conn.get_geometry(msg.window)?.reply() {
|
|
|
|
Ok(geo) => (geo.x.into(), geo.y.into()),
|
|
|
|
Err(err) => {
|
|
|
|
error!(
|
|
|
|
self.log,
|
|
|
|
"Failed to get geometry for {:x}, perhaps the window was already destroyed?",
|
|
|
|
msg.window;
|
|
|
|
"err" => format!("{:?}", err),
|
|
|
|
);
|
|
|
|
(0, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-04 07:17:16 +00:00
|
|
|
let id = msg.data.as_data32()[0];
|
2021-01-04 08:18:35 +00:00
|
|
|
let surface = client.get_resource::<WlSurface>(id);
|
2021-01-04 07:17:16 +00:00
|
|
|
info!(
|
|
|
|
self.log,
|
2021-01-04 08:18:35 +00:00
|
|
|
"X11 surface {:x?} corresponds to WlSurface {:x} = {:?}", msg.window, id, surface,
|
2021-01-04 07:17:16 +00:00
|
|
|
);
|
2021-01-04 08:18:35 +00:00
|
|
|
match surface {
|
|
|
|
None => {
|
2021-01-04 09:07:35 +00:00
|
|
|
self.unpaired_surfaces.insert(id, (msg.window, location));
|
2021-01-04 08:18:35 +00:00
|
|
|
}
|
2021-01-04 09:07:35 +00:00
|
|
|
Some(surface) => self.new_window(msg.window, surface, location),
|
2021-01-04 08:18:35 +00:00
|
|
|
}
|
2021-01-04 07:17:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-01-04 08:18:35 +00:00
|
|
|
|
2021-01-04 09:07:35 +00:00
|
|
|
fn new_window(&mut self, window: Window, surface: WlSurface, location: (i32, i32)) {
|
2021-01-04 08:18:35 +00:00
|
|
|
debug!(self.log, "Matched X11 surface {:x?} to {:x?}", window, surface);
|
2021-01-04 08:46:59 +00:00
|
|
|
|
2021-06-23 07:43:53 +00:00
|
|
|
if give_role(&surface, "x11_surface").is_err() {
|
2021-01-04 08:46:59 +00:00
|
|
|
// It makes no sense to post a protocol error here since that would only kill Xwayland
|
|
|
|
error!(self.log, "Surface {:x?} already has a role?!", surface);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let x11surface = X11Surface { surface };
|
|
|
|
self.window_map
|
|
|
|
.borrow_mut()
|
2021-01-04 09:07:35 +00:00
|
|
|
.insert(Kind::X11(x11surface), location);
|
2021-01-04 08:18:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called when a WlSurface commits.
|
|
|
|
pub fn commit_hook(surface: &WlSurface) {
|
|
|
|
// Is this the Xwayland client?
|
|
|
|
if let Some(client) = surface.as_ref().client() {
|
|
|
|
if let Some(x11) = client.data_map().get::<Rc<RefCell<X11State>>>() {
|
|
|
|
let mut inner = x11.borrow_mut();
|
|
|
|
// Is the surface among the unpaired surfaces (see comment next to WL_SURFACE_ID
|
|
|
|
// handling above)
|
2021-01-04 09:07:35 +00:00
|
|
|
if let Some((window, location)) = inner.unpaired_surfaces.remove(&surface.as_ref().id()) {
|
|
|
|
inner.new_window(window, surface.clone(), location);
|
2021-01-04 08:18:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-04 07:17:16 +00:00
|
|
|
}
|
2021-01-04 08:46:59 +00:00
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct X11Surface {
|
|
|
|
surface: WlSurface,
|
|
|
|
}
|
|
|
|
|
2021-07-01 21:07:07 +00:00
|
|
|
impl std::cmp::PartialEq for X11Surface {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.alive() && other.alive() && self.surface == other.surface
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-04 08:46:59 +00:00
|
|
|
impl X11Surface {
|
|
|
|
pub fn alive(&self) -> bool {
|
|
|
|
self.surface.as_ref().is_alive()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_surface(&self) -> Option<&WlSurface> {
|
|
|
|
if self.alive() {
|
|
|
|
Some(&self.surface)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|