Skip to content

Commit

Permalink
Add an X11 EventSource to calloop
Browse files Browse the repository at this point in the history
This commit creates an event source for calloop that receives X11 events
from the X11 server.

Signed-off-by: Uli Schlachter <psychon@znc.in>
  • Loading branch information
psychon committed Jan 4, 2021
1 parent 207b64b commit c0e322c
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 89 deletions.
2 changes: 1 addition & 1 deletion anvil/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl AnvilState {

#[cfg(feature = "xwayland")]
let _xwayland = {
let xwm = XWm::new();
let xwm = XWm::new(handle.clone(), log.clone());
XWayland::init(xwm, handle.clone(), display.clone(), &mut (), log.clone(), _helper).unwrap()
};

Expand Down
88 changes: 0 additions & 88 deletions anvil/src/xwayland.rs

This file was deleted.

169 changes: 169 additions & 0 deletions anvil/src/xwayland/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use std::{convert::TryFrom, os::unix::net::UnixStream, rc::Rc};

use smithay::{
reexports::{
calloop::LoopHandle,
wayland_server::Client,
},
xwayland::XWindowManager,
};

use x11rb::{
connection::Connection as _,
errors::ReplyOrIdError,
protocol::{
composite::{ConnectionExt as _, Redirect},
xproto::{
ChangeWindowAttributesAux, ConfigWindow, ConfigureWindowAux, ConnectionExt as _, EventMask,
WindowClass,
},
Event,
},
rust_connection::{DefaultStream, RustConnection},
};

use crate::AnvilState;

use x11rb_event_source::X11Source;

mod x11rb_event_source;

/// Implementation of [`smithay::xwayland::XWindowManager`] that is used for starting XWayland.
/// After XWayland was started, the actual state is kept in `X11State`.
pub struct XWm {
handle: LoopHandle<AnvilState>,
log: slog::Logger,
}

impl XWm {
pub fn new(handle: LoopHandle<AnvilState>, log: slog::Logger) -> Self {
Self { handle, log }
}
}

impl XWindowManager for XWm {
fn xwayland_ready(&mut self, connection: UnixStream, _client: Client) {
let (mut wm, source) = X11State::start_wm(connection, self.log.clone()).unwrap();
self.handle
.insert_source(source, move |events, _, _| {
for event in events.into_iter() {
wm.handle_event(event)?;
}
Ok(())
})
.unwrap();
}

fn xwayland_exited(&mut self) {}
}

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,
}

impl X11State {
fn start_wm(connection: UnixStream, log: slog::Logger) -> Result<(Self, X11Source), Box<dyn std::error::Error>> {
// 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,
log,
};

Ok((wm, X11Source::new(conn)))
}

fn handle_event(&mut self, event: Event) -> Result<(), ReplyOrIdError> {
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 {
let id = msg.data.as_data32()[0];
info!(
self.log,
"X11 surface {:x?} corresponds to WlSurface {:x}", msg.window, id,
);
}
}
_ => {}
}
Ok(())
}
}
66 changes: 66 additions & 0 deletions anvil/src/xwayland/x11rb_event_source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::{
io::{Error as IOError, ErrorKind, Result as IOResult},
os::unix::io::AsRawFd,
rc::Rc,
};

use x11rb::{
connection::Connection as _, errors::ReplyOrIdError, protocol::Event, rust_connection::RustConnection,
};

use smithay::reexports::calloop::{
generic::{Fd, Generic},
EventSource, Interest, Mode, Poll, Readiness, Token,
};

pub struct X11Source {
connection: Rc<RustConnection>,
generic: Generic<Fd>,
}

impl X11Source {
pub fn new(connection: Rc<RustConnection>) -> Self {
let fd = Fd(connection.stream().as_raw_fd());
let generic = Generic::new(fd, Interest::Readable, Mode::Level);
Self { connection, generic }
}
}

impl EventSource for X11Source {
type Event = Vec<Event>;
type Metadata = ();
type Ret = Result<(), ReplyOrIdError>;

fn process_events<C>(&mut self, _readiness: Readiness, _token: Token, callback: C) -> IOResult<()>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
fn inner<C>(conn: &RustConnection, mut callback: C) -> Result<(), ReplyOrIdError>
where
C: FnMut(Vec<Event>, &mut ()) -> Result<(), ReplyOrIdError>,
{
let mut events = Vec::new();
while let Some(event) = conn.poll_for_event()? {
events.push(event);
}
if !events.is_empty() {
callback(events, &mut ())?;
}
conn.flush()?;
Ok(())
}
inner(&self.connection, callback).map_err(|err| IOError::new(ErrorKind::Other, err))
}

fn register(&mut self, poll: &mut Poll, token: Token) -> IOResult<()> {
self.generic.register(poll, token)
}

fn reregister(&mut self, poll: &mut Poll, token: Token) -> IOResult<()> {
self.generic.reregister(poll, token)
}

fn unregister(&mut self, poll: &mut Poll) -> IOResult<()> {
self.generic.unregister(poll)
}
}

0 comments on commit c0e322c

Please sign in to comment.