<NME> xserver.rs
<BEF> /*
 * Steps of XWayland server creation
 *
 * Sockets to create:
 * - a pair for XWayland to connect to smithay as a wayland client, we use our
 *   end to insert the XWayland client in the display
 * - a pair for smithay to connect to XWayland as a WM, we give our end to the
 *   WM and it deals with it
 * - 2 listening sockets on which the XWayland server will listen. We need to
 *   bind them ourselves so we know what value put in the $DISPLAY env variable.
 *   This involves some dance with a lockfile to ensure there is no collision with
 *   an other starting xserver
 *   if we listen on display $D, their paths are respectively:
 *   - /tmp/.X11-unix/X$D
 *   - @/tmp/.X11-unix/X$D (abstract socket)
 *
 * The XWayland server is spawned via an intermediate shell
 * -> wlroots does a double-fork while weston a single one, why ??
 *    -> https://stackoverflow.com/questions/881388/
 * -> once it is started, it will check if SIGUSR1 is set to ignored. If so,
 *    if will consider its parent as "smart", and send a SIGUSR1 signal when
 *    startup completes. We want to catch this so we can launch the VM.
 * -> we need to track if the XWayland crashes, to restart it
 *
 * cf https://github.com/swaywm/wlroots/blob/master/xwayland/xwayland.c
 *
 * Setting SIGUSR1 handler is complicated in multithreaded program, because
 * Xwayland will send SIGUSR1 to the process, and if a thread cannot handle
 * SIGUSR1, that thread will be killed.
 *
 * Double-fork can tackle this issue, but this is also very complex in a
 * a multithread program, after forking only signal-safe functions can be used.
 * The only workaround is to fork early before any other thread starts, but
 * doing so will expose an unsafe interface.
 *
 * We use an intermediate shell to translate the signal to simple fd IO.
 * We ask sh to setup SIGUSR1 handler, and in a subshell mute SIGUSR1 and exec
 * Xwayland. When the SIGUSR1 is received, it can communicate to us via redirected
 * STDOUT.
 */
use std::{
    env,
    fmt::Write,
    io::{self, Read},
    os::unix::{
        io::{AsRawFd, RawFd},
        net::UnixStream,
        process::CommandExt,
    },
    process::{ChildStdout, Command, Stdio},
    sync::{Arc, Mutex},
};

};

use calloop::{
    generic::{Fd, Generic},
    Interest, LoopHandle, Mode, RegistrationToken,
};
    Client, DisplayHandle,
};

use slog::{error, info, o};

use super::x11_sockets::{prepare_x11_sockets, X11Lock};

/// The XWayland handle
pub struct XWayland<WM: XWindowManager> {
    inner: Rc<RefCell<Inner<WM>>>,
}

/// Trait to be implemented by you WM for XWayland
///
/// This is a very low-level trait, only notifying you
/// when the connection with XWayland is up, or when
/// it terminates.
///
/// You WM must be able handle the XWayland server connecting
/// then disconnecting several time in a row, but only a single
/// connection will be active at any given time.
pub trait XWindowManager {
    /// The XWayland server is ready
    ///
    /// Your privileged connection to it is this `UnixStream`
    fn xwayland_ready(&mut self, connection: UnixStream, client: Client);
    /// The XWayland server has exited
    fn xwayland_exited(&mut self);
}

impl<WM: XWindowManager + 'static> XWayland<WM> {
    /// Start the XWayland server
    pub fn init<L, T: Any, Data: 'static>(
        wm: WM,
        handle: LoopHandle<'static, Data>,
        display: Rc<RefCell<Display>>,
        data: &mut T,
        logger: L,
    ) -> Result<XWayland<WM>, ()>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(logger);
        let inner = Rc::new(RefCell::new(Inner {
            wm,
            kill_source: {
                let handle = handle.clone();
                Box::new(move |source| handle.kill(source))
            },
            source_maker: Box::new(move |inner, fd| {
                handle
                    .insert_source(
                        Generic::new(Fd(fd), Interest::READ, Mode::Level),
                        move |evt, _, _| {
                            debug_assert!(evt.readable);
                            xwayland_ready(&inner);
                            Ok(())
                        },
                    )
                    .map_err(|_| ())
            }),
            wayland_display: display,
            instance: None,
            log: log.new(o!("smithay_module" => "XWayland")),
        }));
        launch(&inner, data)?;
        Ok(XWayland { inner })
    }
}

impl<WM: XWindowManager> Drop for XWayland<WM> {
    fn drop(&mut self) {
        self.inner.borrow_mut().shutdown();
    }
    }

struct XWaylandInstance {
    display_lock: X11Lock,
    wayland_client: Client,
    startup_handler: Option<RegistrationToken>,
    wm_fd: Option<UnixStream>,
    started_at: ::std::time::Instant,
    child_stdout: Option<ChildStdout>,
}

type SourceMaker<WM> = dyn FnMut(Rc<RefCell<Inner<WM>>>, RawFd) -> Result<RegistrationToken, ()>;

// Inner implementation of the XWayland manager
struct Inner<WM: XWindowManager> {
    wm: WM,
    source_maker: Box<SourceMaker<WM>>,
    wayland_display: Rc<RefCell<Display>>,
    instance: Option<XWaylandInstance>,
    kill_source: Box<dyn Fn(RegistrationToken)>,
    log: ::slog::Logger,
}

// Launch an XWayland server
//
// Does nothing if there is already a launched instance
fn launch<WM: XWindowManager + 'static, T: Any>(
    inner: &Rc<RefCell<Inner<WM>>>,
    data: &mut T,
) -> Result<(), ()> {
    let mut guard = inner.borrow_mut();
    if guard.instance.is_some() {
        return Ok(());
// Inner implementation of the XWayland manager

    info!(guard.log, "Starting XWayland");

    let (x_wm_x11, x_wm_me) = UnixStream::pair().map_err(|_| ())?;
    let (wl_x11, wl_me) = UnixStream::pair().map_err(|_| ())?;

    let (lock, x_fds) = prepare_x11_sockets(guard.log.clone())?;

    // we have now created all the required sockets

    // record launch time
    let creation_time = ::std::time::Instant::now();

    // create the wayland client for XWayland
    let client = unsafe {
        guard
            .wayland_display
            .borrow_mut()
            .create_client(wl_me.into_raw_fd(), data)
    };
    client.data_map().insert_if_missing(|| inner.clone());
    client.add_destructor(Filter::new(|e: Arc<_>, _, mut data| {
        client_destroy::<WM, T>(&e, data.get().unwrap())
    }));

    // all is ready, we can do the fork dance
    let child_stdout = match spawn_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
        Ok(child_stdout) => child_stdout,
        Err(e) => {
            error!(guard.log, "XWayland failed to spawn"; "err" => format!("{:?}", e));
            return Err(());
        }
    };

    let startup_handler = (&mut *guard.source_maker)(inner.clone(), child_stdout.as_raw_fd())?;

    guard.instance = Some(XWaylandInstance {
        display_lock: lock,
        wayland_client: client,
        startup_handler: Some(startup_handler),
        wm_fd: Some(x_wm_me),
        started_at: creation_time,
        child_stdout: Some(child_stdout),
    });

    Ok(())
}

impl<WM: XWindowManager> Inner<WM> {
    // Shutdown the XWayland server and cleanup everything
    fn shutdown(&mut self) {
        // don't do anything if not running
        if let Some(mut instance) = self.instance.take() {
            info!(self.log, "Shutting down XWayland.");
            self.wm.xwayland_exited();
            // kill the client
            instance.wayland_client.kill();
            // remove the event source
            if let Some(s) = instance.startup_handler.take() {
                (self.kill_source)(s);
            }
            // All connections and lockfiles are cleaned by their destructors

            // Remove DISPLAY from the env
        self.channel.register(poll, factory)
    }

    fn reregister(
        &mut self,
        poll: &mut calloop::Poll,
    }
}

fn client_destroy<WM: XWindowManager + 'static, T: Any>(map: &::wayland_server::UserDataMap, data: &mut T) {
    let inner = map.get::<Rc<RefCell<Inner<WM>>>>().unwrap();

    // shutdown the server
    let started_at = inner.borrow().instance.as_ref().map(|i| i.started_at);
    inner.borrow_mut().shutdown();

    // restart it, unless we really just started it, if it crashes right
    // at startup there is no point
    if started_at.map(|t| t.elapsed().as_secs()).unwrap_or(10) > 5 {
        warn!(inner.borrow().log, "XWayland crashed, restarting.");
        let _ = launch(&inner, data);
    } else {
        warn!(
            inner.borrow().log,
            "XWayland crashed less than 5 seconds after its startup, not restarting."
        );
    }
}

fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
    let mut guard = inner.borrow_mut();
    let inner = &mut *guard;
    // instance should never be None at this point
    let instance = inner.instance.as_mut().unwrap();
    let wm = &mut inner.wm;
    // neither the child_stdout
    let child_stdout = instance.child_stdout.as_mut().unwrap();

                .backend_handle()
                .kill_client(instance.wayland_client.id(), DisconnectReason::ConnectionClosed);
    let success = match child_stdout.read(&mut buffer) {
        Ok(len) => len > 0 && buffer[0] == b'S',
        Err(e) => {
            error!(inner.log, "Checking launch status failed"; "err" => format!("{:?}", e));
            false
        }
    };
            ::std::env::remove_var("DISPLAY");
            // We do like wlroots:
            // > We do not kill the XWayland process, it dies to broken pipe
        ::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display()));

        // signal the WM
        info!(inner.log, "XWayland is ready, signaling the WM.");
        wm.xwayland_ready(
            instance.wm_fd.take().unwrap(), // This is a bug if None
            instance.wayland_client.clone(),
        );
    } else {
        error!(
            inner.log,
            "XWayland crashed at startup, will not try to restart it."
        );
    }

    // in all cases, cleanup
    if let Some(s) = instance.startup_handler.take() {
        (inner.kill_source)(s);
    }
}

            false
        }
    };

    if success {
        // setup the environment
        ::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display()));

        // signal the WM
        info!(
            guard.log,
            "XWayland is ready on DISPLAY \":{}\", signaling the WM.",
            instance.display_lock.display()
        );
        // send error occurs if the user dropped the channel... We cannot do much except ignore.
        let _ = guard.sender.send(XWaylandEvent::Ready {
            connection: instance.wm_fd.take().unwrap(), // This is a bug if None
            client: instance.wayland_client.clone(),
            client_fd: instance.wayland_client_fd,
            display: instance.display_lock.display(),
        });
    } else {
        error!(
            guard.log,
            "XWayland crashed at startup, will not try to restart it."
        );
    }
}

/// Spawn XWayland with given sockets on given display
///
/// Returns a pipe that outputs 'S' upon successful launch.
fn spawn_xwayland(
    display: u32,
    wayland_socket: UnixStream,
    wm_socket: UnixStream,
    listen_sockets: &[UnixStream],
) -> io::Result<ChildStdout> {
    let mut command = Command::new("sh");

    // We use output stream to communicate because FD is easier to handle than exit code.
    command.stdout(Stdio::piped());

    let mut xwayland_args = format!(":{} -rootless -terminate -wm {}", display, wm_socket.as_raw_fd());
    for socket in listen_sockets {
        // Will only fail to write on OOM, so this panic is fine.
        write!(xwayland_args, " -listenfd {}", socket.as_raw_fd()).unwrap();
    }
    // This command let sh to:
    // * Set up signal handler for USR1
    // * Launch Xwayland with USR1 ignored so Xwayland will signal us when it is ready (also redirect
    //   Xwayland's STDOUT to STDERR so its output, if any, won't distract us)
    // * Print "S" and exit if USR1 is received
    command.arg("-c").arg(format!(
        "trap 'echo S' USR1; (trap '' USR1; exec Xwayland {}) 1>&2 & wait",
        xwayland_args
    ));

    // Setup the environment: clear everything except PATH and XDG_RUNTIME_DIR
    command.env_clear();
    for (key, value) in env::vars_os() {
        if key.to_str() == Some("PATH") || key.to_str() == Some("XDG_RUNTIME_DIR") {
            command.env(key, value);
            continue;
        }
    }
    command.env("WAYLAND_SOCKET", format!("{}", wayland_socket.as_raw_fd()));

    unsafe {
        let wayland_socket_fd = wayland_socket.as_raw_fd();
        let wm_socket_fd = wm_socket.as_raw_fd();
        let socket_fds: Vec<_> = listen_sockets.iter().map(|socket| socket.as_raw_fd()).collect();
        command.pre_exec(move || {
            // unset the CLOEXEC flag from the sockets we need to pass
            // to xwayland
            unset_cloexec(wayland_socket_fd)?;
            unset_cloexec(wm_socket_fd)?;
            for &socket in socket_fds.iter() {
                unset_cloexec(socket)?;
            }
            Ok(())
        });
    }

    let mut child = command.spawn()?;
    Ok(child.stdout.take().expect("stdout should be piped"))
}

/// Remove the `O_CLOEXEC` flag from this `Fd`
///
/// This means that the `Fd` will *not* be automatically
/// closed when we `exec()` into XWayland
fn unset_cloexec(fd: RawFd) -> io::Result<()> {
    use nix::fcntl::{fcntl, FcntlArg, FdFlag};
    fcntl(fd, FcntlArg::F_SETFD(FdFlag::empty()))?;
    Ok(())
}

<MSG> xwayland: rework as an EventSource

Reorganize the XWayland abstraction into a calloop EventLoop.

Fixes #245
Fixes #203

<DFF> @@ -54,6 +54,7 @@ use std::{
 };
 
 use calloop::{
+    channel::{sync_channel, Channel, SyncSender},
     generic::{Fd, Generic},
     Interest, LoopHandle, Mode, RegistrationToken,
 };
@@ -65,69 +66,75 @@ use wayland_server::{Client, Display, Filter};
 use super::x11_sockets::{prepare_x11_sockets, X11Lock};
 
 /// The XWayland handle
-pub struct XWayland<WM: XWindowManager> {
-    inner: Rc<RefCell<Inner<WM>>>,
+pub struct XWayland<Data> {
+    inner: Rc<RefCell<Inner<Data>>>,
 }
 
-/// Trait to be implemented by you WM for XWayland
+/// Events generated by the XWayland manager
 ///
-/// This is a very low-level trait, only notifying you
-/// when the connection with XWayland is up, or when
-/// it terminates.
+/// This is a very low-level interface, only notifying you when the connection
+/// with XWayland is up, or when it terminates.
 ///
-/// You WM must be able handle the XWayland server connecting
-/// then disconnecting several time in a row, but only a single
-/// connection will be active at any given time.
-pub trait XWindowManager {
+/// Your WM code must be able handle the XWayland server connecting then
+/// disconnecting several time in a row, but only a single connection will
+/// be active at any given time.
+pub enum XWaylandEvent {
     /// The XWayland server is ready
-    ///
-    /// Your privileged connection to it is this `UnixStream`
-    fn xwayland_ready(&mut self, connection: UnixStream, client: Client);
-    /// The XWayland server has exited
-    fn xwayland_exited(&mut self);
+    Ready {
+        /// Privileged X11 connection to XWayland
+        connection: UnixStream,
+        /// Wayland client representing XWayland
+        client: Client,
+    },
+    /// The XWayland server exited
+    Exited,
 }
 
-impl<WM: XWindowManager + 'static> XWayland<WM> {
-    /// Start the XWayland server
-    pub fn init<L, T: Any, Data: 'static>(
-        wm: WM,
+impl<Data: Any + 'static> XWayland<Data> {
+    /// Create a new XWayland manager
+    pub fn new<L>(
         handle: LoopHandle<'static, Data>,
         display: Rc<RefCell<Display>>,
-        data: &mut T,
         logger: L,
-    ) -> Result<XWayland<WM>, ()>
+    ) -> (XWayland<Data>, XWaylandSource)
     where
         L: Into<Option<::slog::Logger>>,
     {
         let log = crate::slog_or_fallback(logger);
+        // We don't expect to ever have more than 2 messages in flight, if XWayland got ready and then died right away
+        let (sender, channel) = sync_channel(2);
         let inner = Rc::new(RefCell::new(Inner {
-            wm,
-            kill_source: {
-                let handle = handle.clone();
-                Box::new(move |source| handle.kill(source))
-            },
-            source_maker: Box::new(move |inner, fd| {
-                handle
-                    .insert_source(
-                        Generic::new(Fd(fd), Interest::READ, Mode::Level),
-                        move |evt, _, _| {
-                            debug_assert!(evt.readable);
-                            xwayland_ready(&inner);
-                            Ok(())
-                        },
-                    )
-                    .map_err(|_| ())
-            }),
+            handle,
             wayland_display: display,
             instance: None,
+            sender,
             log: log.new(o!("smithay_module" => "XWayland")),
         }));
-        launch(&inner, data)?;
-        Ok(XWayland { inner })
+        (XWayland { inner }, XWaylandSource { channel })
+    }
+
+    /// Attempt to start the XWayland instance
+    ///
+    /// If it succeeds, you'll eventually receive an `XWaylandEvent::Ready`
+    /// through the source provided by `XWayland::new()` containing an
+    /// `UnixStream` representing your WM connection to XWayland, and the
+    /// wayland `Client` for XWayland.
+    ///
+    /// Does nothing if XWayland is already started or starting.
+    pub fn start(&self) -> std::io::Result<()> {
+        launch(&self.inner)
+    }
+
+    /// Shutdown XWayland
+    ///
+    /// Does nothing if it was not already running, otherwise kills it and you will
+    /// later receive a `XWaylandEvent::Exited` event.
+    pub fn shutdown(&self) {
+        self.inner.borrow_mut().shutdown();
     }
 }
 
-impl<WM: XWindowManager> Drop for XWayland<WM> {
+impl<Data> Drop for XWayland<Data> {
     fn drop(&mut self) {
         self.inner.borrow_mut().shutdown();
     }
@@ -135,32 +142,25 @@ impl<WM: XWindowManager> Drop for XWayland<WM> {
 
 struct XWaylandInstance {
     display_lock: X11Lock,
-    wayland_client: Client,
+    wayland_client: Option<Client>,
     startup_handler: Option<RegistrationToken>,
     wm_fd: Option<UnixStream>,
-    started_at: ::std::time::Instant,
     child_stdout: Option<ChildStdout>,
 }
 
-type SourceMaker<WM> = dyn FnMut(Rc<RefCell<Inner<WM>>>, RawFd) -> Result<RegistrationToken, ()>;
-
 // Inner implementation of the XWayland manager
-struct Inner<WM: XWindowManager> {
-    wm: WM,
-    source_maker: Box<SourceMaker<WM>>,
+struct Inner<Data> {
+    sender: SyncSender<XWaylandEvent>,
+    handle: LoopHandle<'static, Data>,
     wayland_display: Rc<RefCell<Display>>,
     instance: Option<XWaylandInstance>,
-    kill_source: Box<dyn Fn(RegistrationToken)>,
     log: ::slog::Logger,
 }
 
 // Launch an XWayland server
 //
 // Does nothing if there is already a launched instance
-fn launch<WM: XWindowManager + 'static, T: Any>(
-    inner: &Rc<RefCell<Inner<WM>>>,
-    data: &mut T,
-) -> Result<(), ()> {
+fn launch<Data: Any>(inner: &Rc<RefCell<Inner<Data>>>) -> std::io::Result<()> {
     let mut guard = inner.borrow_mut();
     if guard.instance.is_some() {
         return Ok(());
@@ -168,64 +168,119 @@ fn launch<WM: XWindowManager + 'static, T: Any>(
 
     info!(guard.log, "Starting XWayland");
 
-    let (x_wm_x11, x_wm_me) = UnixStream::pair().map_err(|_| ())?;
-    let (wl_x11, wl_me) = UnixStream::pair().map_err(|_| ())?;
+    let (x_wm_x11, x_wm_me) = UnixStream::pair()?;
+    let (wl_x11, wl_me) = UnixStream::pair()?;
 
     let (lock, x_fds) = prepare_x11_sockets(guard.log.clone())?;
 
     // we have now created all the required sockets
 
-    // record launch time
-    let creation_time = ::std::time::Instant::now();
-
-    // create the wayland client for XWayland
-    let client = unsafe {
-        guard
-            .wayland_display
-            .borrow_mut()
-            .create_client(wl_me.into_raw_fd(), data)
-    };
-    client.data_map().insert_if_missing(|| inner.clone());
-    client.add_destructor(Filter::new(|e: Arc<_>, _, mut data| {
-        client_destroy::<WM, T>(&e, data.get().unwrap())
-    }));
+    // Setup the associated wayland client to be created in an idle callback, so that we don't need
+    // to access the dispatch_data *right now*
+    let idle_inner = inner.clone();
+    guard.handle.insert_idle(move |data| {
+        let mut guard = idle_inner.borrow_mut();
+        let guard = &mut *guard;
+        if let Some(ref mut instance) = guard.instance {
+            // create the wayland client for XWayland
+            let client = unsafe {
+                guard
+                    .wayland_display
+                    .borrow_mut()
+                    .create_client(wl_me.into_raw_fd(), data)
+            };
+            client.data_map().insert_if_missing(|| idle_inner.clone());
+            client.add_destructor(Filter::new(|e: Arc<_>, _, _| client_destroy::<Data>(&e)));
+
+            instance.wayland_client = Some(client.clone());
+        }
+    });
 
     // all is ready, we can do the fork dance
     let child_stdout = match spawn_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
         Ok(child_stdout) => child_stdout,
         Err(e) => {
             error!(guard.log, "XWayland failed to spawn"; "err" => format!("{:?}", e));
-            return Err(());
+            return Err(e);
         }
     };
 
-    let startup_handler = (&mut *guard.source_maker)(inner.clone(), child_stdout.as_raw_fd())?;
+    let inner = inner.clone();
+    let startup_handler = guard.handle.insert_source(
+        Generic::new(Fd(child_stdout.as_raw_fd()), Interest::READ, Mode::Level),
+        move |_, _, _| {
+            // the closure must be called exactly one time, this cannot panic
+            xwayland_ready(&inner);
+            Ok(())
+        },
+    )?;
 
     guard.instance = Some(XWaylandInstance {
         display_lock: lock,
-        wayland_client: client,
         startup_handler: Some(startup_handler),
+        wayland_client: None,
         wm_fd: Some(x_wm_me),
-        started_at: creation_time,
         child_stdout: Some(child_stdout),
     });
 
     Ok(())
 }
 
-impl<WM: XWindowManager> Inner<WM> {
+pub struct XWaylandSource {
+    channel: Channel<XWaylandEvent>,
+}
+
+impl calloop::EventSource for XWaylandSource {
+    type Event = XWaylandEvent;
+    type Metadata = ();
+    type Ret = ();
+
+    fn process_events<F>(
+        &mut self,
+        readiness: calloop::Readiness,
+        token: calloop::Token,
+        mut callback: F,
+    ) -> std::io::Result<()>
+    where
+        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
+    {
+        self.channel
+            .process_events(readiness, token, |event, &mut ()| match event {
+                calloop::channel::Event::Msg(msg) => callback(msg, &mut ()),
+                calloop::channel::Event::Closed => {}
+            })
+    }
+
+    fn register(&mut self, poll: &mut calloop::Poll, token: calloop::Token) -> std::io::Result<()> {
+        self.channel.register(poll, token)
+    }
+
+    fn reregister(&mut self, poll: &mut calloop::Poll, token: calloop::Token) -> std::io::Result<()> {
+        self.channel.reregister(poll, token)
+    }
+
+    fn unregister(&mut self, poll: &mut calloop::Poll) -> std::io::Result<()> {
+        self.channel.unregister(poll)
+    }
+}
+
+impl<Data> Inner<Data> {
     // Shutdown the XWayland server and cleanup everything
     fn shutdown(&mut self) {
         // don't do anything if not running
         if let Some(mut instance) = self.instance.take() {
             info!(self.log, "Shutting down XWayland.");
-            self.wm.xwayland_exited();
             // kill the client
-            instance.wayland_client.kill();
+            if let Some(client) = instance.wayland_client {
+                client.kill();
+            }
             // remove the event source
             if let Some(s) = instance.startup_handler.take() {
-                (self.kill_source)(s);
+                self.handle.kill(s);
             }
+            // send error occurs if the user dropped the channel... We cannot do much except ignore.
+            let _ = self.sender.send(XWaylandEvent::Exited);
+
             // All connections and lockfiles are cleaned by their destructors
 
             // Remove DISPLAY from the env
@@ -238,32 +293,17 @@ impl<WM: XWindowManager> Inner<WM> {
     }
 }
 
-fn client_destroy<WM: XWindowManager + 'static, T: Any>(map: &::wayland_server::UserDataMap, data: &mut T) {
-    let inner = map.get::<Rc<RefCell<Inner<WM>>>>().unwrap();
-
-    // shutdown the server
-    let started_at = inner.borrow().instance.as_ref().map(|i| i.started_at);
+fn client_destroy<Data: 'static>(map: &::wayland_server::UserDataMap) {
+    let inner = map.get::<Rc<RefCell<Inner<Data>>>>().unwrap();
     inner.borrow_mut().shutdown();
-
-    // restart it, unless we really just started it, if it crashes right
-    // at startup there is no point
-    if started_at.map(|t| t.elapsed().as_secs()).unwrap_or(10) > 5 {
-        warn!(inner.borrow().log, "XWayland crashed, restarting.");
-        let _ = launch(&inner, data);
-    } else {
-        warn!(
-            inner.borrow().log,
-            "XWayland crashed less than 5 seconds after its startup, not restarting."
-        );
-    }
 }
 
-fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
+fn xwayland_ready<Data: 'static>(inner: &Rc<RefCell<Inner<Data>>>) {
+    // Lots of re-borrowing to please the borrow-checker
     let mut guard = inner.borrow_mut();
-    let inner = &mut *guard;
+    let guard = &mut *guard;
     // instance should never be None at this point
-    let instance = inner.instance.as_mut().unwrap();
-    let wm = &mut inner.wm;
+    let instance = guard.instance.as_mut().unwrap();
     // neither the child_stdout
     let child_stdout = instance.child_stdout.as_mut().unwrap();
 
@@ -272,7 +312,7 @@ fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
     let success = match child_stdout.read(&mut buffer) {
         Ok(len) => len > 0 && buffer[0] == b'S',
         Err(e) => {
-            error!(inner.log, "Checking launch status failed"; "err" => format!("{:?}", e));
+            error!(guard.log, "Checking launch status failed"; "err" => format!("{:?}", e));
             false
         }
     };
@@ -282,21 +322,22 @@ fn xwayland_ready<WM: XWindowManager>(inner: &Rc<RefCell<Inner<WM>>>) {
         ::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display()));
 
         // signal the WM
-        info!(inner.log, "XWayland is ready, signaling the WM.");
-        wm.xwayland_ready(
-            instance.wm_fd.take().unwrap(), // This is a bug if None
-            instance.wayland_client.clone(),
-        );
+        info!(guard.log, "XWayland is ready, signaling the WM.");
+        // send error occurs if the user dropped the channel... We cannot do much except ignore.
+        let _ = guard.sender.send(XWaylandEvent::Ready {
+            connection: instance.wm_fd.take().unwrap(), // This is a bug if None
+            client: instance.wayland_client.clone().unwrap(),
+        });
     } else {
         error!(
-            inner.log,
+            guard.log,
             "XWayland crashed at startup, will not try to restart it."
         );
     }
 
     // in all cases, cleanup
     if let Some(s) = instance.startup_handler.take() {
-        (inner.kill_source)(s);
+        guard.handle.kill(s);
     }
 }
 

xwayland: rework as an EventSource