xwayland: rework as an EventSource
Reorganize the XWayland abstraction into a calloop EventLoop. Fixes #245 Fixes #203
This commit is contained in:
parent
01b5c1a183
commit
671e2053e9
|
@ -23,11 +23,9 @@ use smithay::{
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
use smithay::backend::egl::display::EGLBufferReader;
|
use smithay::backend::egl::display::EGLBufferReader;
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
use smithay::xwayland::XWayland;
|
use smithay::xwayland::{XWayland, XWaylandEvent};
|
||||||
|
|
||||||
use crate::shell::init_shell;
|
use crate::shell::init_shell;
|
||||||
#[cfg(feature = "xwayland")]
|
|
||||||
use crate::xwayland::XWm;
|
|
||||||
|
|
||||||
pub struct AnvilState<BackendData> {
|
pub struct AnvilState<BackendData> {
|
||||||
pub backend_data: BackendData,
|
pub backend_data: BackendData,
|
||||||
|
@ -48,9 +46,8 @@ pub struct AnvilState<BackendData> {
|
||||||
pub start_time: std::time::Instant,
|
pub start_time: std::time::Instant,
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
pub egl_reader: Option<EGLBufferReader>,
|
pub egl_reader: Option<EGLBufferReader>,
|
||||||
// things we must keep alive
|
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
_xwayland: XWayland<XWm<BackendData>>,
|
pub xwayland: XWayland<AnvilState<BackendData>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
||||||
|
@ -141,14 +138,19 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
||||||
.expect("Failed to initialize the keyboard");
|
.expect("Failed to initialize the keyboard");
|
||||||
|
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
let _xwayland = {
|
let xwayland = {
|
||||||
let xwm = XWm::new(
|
let (xwayland, channel) = XWayland::new(handle.clone(), display.clone(), log.clone());
|
||||||
handle.clone(),
|
let ret = handle.insert_source(channel, |event, _, anvil_state| match event {
|
||||||
shell_handles.token,
|
XWaylandEvent::Ready { connection, client } => anvil_state.xwayland_ready(connection, client),
|
||||||
shell_handles.window_map.clone(),
|
XWaylandEvent::Exited => anvil_state.xwayland_exited(),
|
||||||
log.clone(),
|
});
|
||||||
|
if let Err(e) = ret {
|
||||||
|
error!(
|
||||||
|
log,
|
||||||
|
"Failed to insert the XWaylandSource into the event loop: {}", e
|
||||||
);
|
);
|
||||||
XWayland::init(xwm, handle.clone(), display.clone(), &mut (), log.clone()).unwrap()
|
}
|
||||||
|
xwayland
|
||||||
};
|
};
|
||||||
|
|
||||||
AnvilState {
|
AnvilState {
|
||||||
|
@ -170,7 +172,7 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
|
||||||
egl_reader,
|
egl_reader,
|
||||||
start_time: std::time::Instant::now(),
|
start_time: std::time::Instant::now(),
|
||||||
#[cfg(feature = "xwayland")]
|
#[cfg(feature = "xwayland")]
|
||||||
_xwayland,
|
xwayland,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,6 +219,12 @@ pub fn run_udev(
|
||||||
.map_err(|e| -> IoError { e.into() })
|
.map_err(|e| -> IoError { e.into() })
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start XWayland if supported
|
||||||
|
*/
|
||||||
|
#[cfg(feature = "xwayland")]
|
||||||
|
state.start_xwayland();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And run our loop
|
* And run our loop
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -98,6 +98,9 @@ pub fn run_winit(
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
let mut cursor_visible = true;
|
let mut cursor_visible = true;
|
||||||
|
|
||||||
|
#[cfg(feature = "xwayland")]
|
||||||
|
state.start_xwayland();
|
||||||
|
|
||||||
info!(log, "Initialization completed, starting the main loop.");
|
info!(log, "Initialization completed, starting the main loop.");
|
||||||
|
|
||||||
while state.running.load(Ordering::SeqCst) {
|
while state.running.load(Ordering::SeqCst) {
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, convert::TryFrom, os::unix::net::UnixStream, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, convert::TryFrom, os::unix::net::UnixStream, rc::Rc};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
reexports::{
|
reexports::wayland_server::{protocol::wl_surface::WlSurface, Client},
|
||||||
calloop::LoopHandle,
|
|
||||||
wayland_server::{protocol::wl_surface::WlSurface, Client},
|
|
||||||
},
|
|
||||||
wayland::compositor::CompositorToken,
|
wayland::compositor::CompositorToken,
|
||||||
xwayland::XWindowManager,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use x11rb::{
|
use x11rb::{
|
||||||
|
@ -33,35 +29,16 @@ use x11rb_event_source::X11Source;
|
||||||
|
|
||||||
mod x11rb_event_source;
|
mod x11rb_event_source;
|
||||||
|
|
||||||
/// Implementation of [`smithay::xwayland::XWindowManager`] that is used for starting XWayland.
|
impl<BackendData: 'static> AnvilState<BackendData> {
|
||||||
/// After XWayland was started, the actual state is kept in `X11State`.
|
pub fn start_xwayland(&mut self) {
|
||||||
pub struct XWm<Backend> {
|
if let Err(e) = self.xwayland.start() {
|
||||||
handle: LoopHandle<'static, AnvilState<Backend>>,
|
error!(self.log, "Failed to start XWayland: {}", e);
|
||||||
token: CompositorToken<Roles>,
|
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
|
||||||
log: slog::Logger,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Backend> XWm<Backend> {
|
|
||||||
pub fn new(
|
|
||||||
handle: LoopHandle<'static, AnvilState<Backend>>,
|
|
||||||
token: CompositorToken<Roles>,
|
|
||||||
window_map: Rc<RefCell<MyWindowMap>>,
|
|
||||||
log: slog::Logger,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
handle,
|
|
||||||
token,
|
|
||||||
window_map,
|
|
||||||
log,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<Backend> XWindowManager for XWm<Backend> {
|
pub fn xwayland_ready(&mut self, connection: UnixStream, client: Client) {
|
||||||
fn xwayland_ready(&mut self, connection: UnixStream, client: Client) {
|
|
||||||
let (wm, source) =
|
let (wm, source) =
|
||||||
X11State::start_wm(connection, self.token, self.window_map.clone(), self.log.clone()).unwrap();
|
X11State::start_wm(connection, self.ctoken, self.window_map.clone(), self.log.clone()).unwrap();
|
||||||
let wm = Rc::new(RefCell::new(wm));
|
let wm = Rc::new(RefCell::new(wm));
|
||||||
client.data_map().insert_if_missing(|| Rc::clone(&wm));
|
client.data_map().insert_if_missing(|| Rc::clone(&wm));
|
||||||
self.handle
|
self.handle
|
||||||
|
@ -75,7 +52,9 @@ impl<Backend> XWindowManager for XWm<Backend> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xwayland_exited(&mut self) {}
|
pub fn xwayland_exited(&mut self) {
|
||||||
|
error!(self.log, "Xwayland crashed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x11rb::atom_manager! {
|
x11rb::atom_manager! {
|
||||||
|
|
|
@ -6,14 +6,13 @@
|
||||||
//! The starting point is the [`XWayland`](struct.XWayland.html) struct, which represents the
|
//! The starting point is the [`XWayland`](struct.XWayland.html) struct, which represents the
|
||||||
//! running `XWayland` instance. Dropping it will shutdown XWayland.
|
//! running `XWayland` instance. Dropping it will shutdown XWayland.
|
||||||
//!
|
//!
|
||||||
//! You need to provide an implementation of the `XWindowManager` trait which gives you
|
//! You need to provide an implementation of a X11 Window Manager for XWayland to
|
||||||
//! access to the X11 WM connection and the `Client` associated with XWayland. You'll need
|
//! function properly. You'll need to treat XWayland (and all its X11 apps) as one
|
||||||
//! to treat XWayland (and all its X11 apps) as one special client, and play the role of
|
//! special client, and play the role of an X11 Window Manager.
|
||||||
//! an X11 Window Manager.
|
|
||||||
//!
|
//!
|
||||||
//! Smithay does not provide any helper for doing that yet, but it is planned.
|
//! Smithay does not provide any helper for doing that yet, but it is planned.
|
||||||
|
|
||||||
mod x11_sockets;
|
mod x11_sockets;
|
||||||
mod xserver;
|
mod xserver;
|
||||||
|
|
||||||
pub use self::xserver::{XWayland, XWindowManager};
|
pub use self::xserver::{XWayland, XWaylandEvent};
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
use nix::{errno::Errno, sys::socket, Error as NixError, Result as NixResult};
|
use nix::{errno::Errno, sys::socket, Error as NixError, Result as NixResult};
|
||||||
|
|
||||||
/// Find a free X11 display slot and setup
|
/// Find a free X11 display slot and setup
|
||||||
pub(crate) fn prepare_x11_sockets(log: ::slog::Logger) -> Result<(X11Lock, [UnixStream; 2]), ()> {
|
pub(crate) fn prepare_x11_sockets(log: ::slog::Logger) -> Result<(X11Lock, [UnixStream; 2]), std::io::Error> {
|
||||||
for d in 0..33 {
|
for d in 0..33 {
|
||||||
// if fails, try the next one
|
// if fails, try the next one
|
||||||
if let Ok(lock) = X11Lock::grab(d, log.clone()) {
|
if let Ok(lock) = X11Lock::grab(d, log.clone()) {
|
||||||
|
@ -18,7 +18,10 @@ pub(crate) fn prepare_x11_sockets(log: ::slog::Logger) -> Result<(X11Lock, [Unix
|
||||||
}
|
}
|
||||||
// If we reach here, all values from 0 to 32 failed
|
// If we reach here, all values from 0 to 32 failed
|
||||||
// we need to stop trying at some point
|
// we need to stop trying at some point
|
||||||
Err(())
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::AddrInUse,
|
||||||
|
"Could not find a free socket for the XServer.",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct X11Lock {
|
pub(crate) struct X11Lock {
|
||||||
|
|
|
@ -54,6 +54,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use calloop::{
|
use calloop::{
|
||||||
|
channel::{sync_channel, Channel, SyncSender},
|
||||||
generic::{Fd, Generic},
|
generic::{Fd, Generic},
|
||||||
Interest, LoopHandle, Mode, RegistrationToken,
|
Interest, LoopHandle, Mode, RegistrationToken,
|
||||||
};
|
};
|
||||||
|
@ -65,69 +66,75 @@ use wayland_server::{Client, Display, Filter};
|
||||||
use super::x11_sockets::{prepare_x11_sockets, X11Lock};
|
use super::x11_sockets::{prepare_x11_sockets, X11Lock};
|
||||||
|
|
||||||
/// The XWayland handle
|
/// The XWayland handle
|
||||||
pub struct XWayland<WM: XWindowManager> {
|
pub struct XWayland<Data> {
|
||||||
inner: Rc<RefCell<Inner<WM>>>,
|
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
|
/// This is a very low-level interface, only notifying you when the connection
|
||||||
/// when the connection with XWayland is up, or when
|
/// with XWayland is up, or when it terminates.
|
||||||
/// it terminates.
|
|
||||||
///
|
///
|
||||||
/// You WM must be able handle the XWayland server connecting
|
/// Your WM code must be able handle the XWayland server connecting then
|
||||||
/// then disconnecting several time in a row, but only a single
|
/// disconnecting several time in a row, but only a single connection will
|
||||||
/// connection will be active at any given time.
|
/// be active at any given time.
|
||||||
pub trait XWindowManager {
|
pub enum XWaylandEvent {
|
||||||
/// The XWayland server is ready
|
/// The XWayland server is ready
|
||||||
///
|
Ready {
|
||||||
/// Your privileged connection to it is this `UnixStream`
|
/// Privileged X11 connection to XWayland
|
||||||
fn xwayland_ready(&mut self, connection: UnixStream, client: Client);
|
connection: UnixStream,
|
||||||
/// The XWayland server has exited
|
/// Wayland client representing XWayland
|
||||||
fn xwayland_exited(&mut self);
|
client: Client,
|
||||||
|
},
|
||||||
|
/// The XWayland server exited
|
||||||
|
Exited,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<WM: XWindowManager + 'static> XWayland<WM> {
|
impl<Data: Any + 'static> XWayland<Data> {
|
||||||
/// Start the XWayland server
|
/// Create a new XWayland manager
|
||||||
pub fn init<L, T: Any, Data: 'static>(
|
pub fn new<L>(
|
||||||
wm: WM,
|
|
||||||
handle: LoopHandle<'static, Data>,
|
handle: LoopHandle<'static, Data>,
|
||||||
display: Rc<RefCell<Display>>,
|
display: Rc<RefCell<Display>>,
|
||||||
data: &mut T,
|
|
||||||
logger: L,
|
logger: L,
|
||||||
) -> Result<XWayland<WM>, ()>
|
) -> (XWayland<Data>, XWaylandSource)
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
let log = crate::slog_or_fallback(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 {
|
let inner = Rc::new(RefCell::new(Inner {
|
||||||
wm,
|
handle,
|
||||||
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,
|
wayland_display: display,
|
||||||
instance: None,
|
instance: None,
|
||||||
|
sender,
|
||||||
log: log.new(o!("smithay_module" => "XWayland")),
|
log: log.new(o!("smithay_module" => "XWayland")),
|
||||||
}));
|
}));
|
||||||
launch(&inner, data)?;
|
(XWayland { inner }, XWaylandSource { channel })
|
||||||
Ok(XWayland { inner })
|
}
|
||||||
|
|
||||||
|
/// 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) {
|
fn drop(&mut self) {
|
||||||
self.inner.borrow_mut().shutdown();
|
self.inner.borrow_mut().shutdown();
|
||||||
}
|
}
|
||||||
|
@ -135,32 +142,25 @@ impl<WM: XWindowManager> Drop for XWayland<WM> {
|
||||||
|
|
||||||
struct XWaylandInstance {
|
struct XWaylandInstance {
|
||||||
display_lock: X11Lock,
|
display_lock: X11Lock,
|
||||||
wayland_client: Client,
|
wayland_client: Option<Client>,
|
||||||
startup_handler: Option<RegistrationToken>,
|
startup_handler: Option<RegistrationToken>,
|
||||||
wm_fd: Option<UnixStream>,
|
wm_fd: Option<UnixStream>,
|
||||||
started_at: ::std::time::Instant,
|
|
||||||
child_stdout: Option<ChildStdout>,
|
child_stdout: Option<ChildStdout>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type SourceMaker<WM> = dyn FnMut(Rc<RefCell<Inner<WM>>>, RawFd) -> Result<RegistrationToken, ()>;
|
|
||||||
|
|
||||||
// Inner implementation of the XWayland manager
|
// Inner implementation of the XWayland manager
|
||||||
struct Inner<WM: XWindowManager> {
|
struct Inner<Data> {
|
||||||
wm: WM,
|
sender: SyncSender<XWaylandEvent>,
|
||||||
source_maker: Box<SourceMaker<WM>>,
|
handle: LoopHandle<'static, Data>,
|
||||||
wayland_display: Rc<RefCell<Display>>,
|
wayland_display: Rc<RefCell<Display>>,
|
||||||
instance: Option<XWaylandInstance>,
|
instance: Option<XWaylandInstance>,
|
||||||
kill_source: Box<dyn Fn(RegistrationToken)>,
|
|
||||||
log: ::slog::Logger,
|
log: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch an XWayland server
|
// Launch an XWayland server
|
||||||
//
|
//
|
||||||
// Does nothing if there is already a launched instance
|
// Does nothing if there is already a launched instance
|
||||||
fn launch<WM: XWindowManager + 'static, T: Any>(
|
fn launch<Data: Any>(inner: &Rc<RefCell<Inner<Data>>>) -> std::io::Result<()> {
|
||||||
inner: &Rc<RefCell<Inner<WM>>>,
|
|
||||||
data: &mut T,
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
let mut guard = inner.borrow_mut();
|
let mut guard = inner.borrow_mut();
|
||||||
if guard.instance.is_some() {
|
if guard.instance.is_some() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -168,16 +168,20 @@ fn launch<WM: XWindowManager + 'static, T: Any>(
|
||||||
|
|
||||||
info!(guard.log, "Starting XWayland");
|
info!(guard.log, "Starting XWayland");
|
||||||
|
|
||||||
let (x_wm_x11, x_wm_me) = UnixStream::pair().map_err(|_| ())?;
|
let (x_wm_x11, x_wm_me) = UnixStream::pair()?;
|
||||||
let (wl_x11, wl_me) = UnixStream::pair().map_err(|_| ())?;
|
let (wl_x11, wl_me) = UnixStream::pair()?;
|
||||||
|
|
||||||
let (lock, x_fds) = prepare_x11_sockets(guard.log.clone())?;
|
let (lock, x_fds) = prepare_x11_sockets(guard.log.clone())?;
|
||||||
|
|
||||||
// we have now created all the required sockets
|
// we have now created all the required sockets
|
||||||
|
|
||||||
// record launch time
|
// Setup the associated wayland client to be created in an idle callback, so that we don't need
|
||||||
let creation_time = ::std::time::Instant::now();
|
// 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
|
// create the wayland client for XWayland
|
||||||
let client = unsafe {
|
let client = unsafe {
|
||||||
guard
|
guard
|
||||||
|
@ -185,47 +189,98 @@ fn launch<WM: XWindowManager + 'static, T: Any>(
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.create_client(wl_me.into_raw_fd(), data)
|
.create_client(wl_me.into_raw_fd(), data)
|
||||||
};
|
};
|
||||||
client.data_map().insert_if_missing(|| inner.clone());
|
client.data_map().insert_if_missing(|| idle_inner.clone());
|
||||||
client.add_destructor(Filter::new(|e: Arc<_>, _, mut data| {
|
client.add_destructor(Filter::new(|e: Arc<_>, _, _| client_destroy::<Data>(&e)));
|
||||||
client_destroy::<WM, T>(&e, data.get().unwrap())
|
|
||||||
}));
|
instance.wayland_client = Some(client.clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// all is ready, we can do the fork dance
|
// all is ready, we can do the fork dance
|
||||||
let child_stdout = match spawn_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
|
let child_stdout = match spawn_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
|
||||||
Ok(child_stdout) => child_stdout,
|
Ok(child_stdout) => child_stdout,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(guard.log, "XWayland failed to spawn"; "err" => format!("{:?}", 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 {
|
guard.instance = Some(XWaylandInstance {
|
||||||
display_lock: lock,
|
display_lock: lock,
|
||||||
wayland_client: client,
|
|
||||||
startup_handler: Some(startup_handler),
|
startup_handler: Some(startup_handler),
|
||||||
|
wayland_client: None,
|
||||||
wm_fd: Some(x_wm_me),
|
wm_fd: Some(x_wm_me),
|
||||||
started_at: creation_time,
|
|
||||||
child_stdout: Some(child_stdout),
|
child_stdout: Some(child_stdout),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
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
|
// Shutdown the XWayland server and cleanup everything
|
||||||
fn shutdown(&mut self) {
|
fn shutdown(&mut self) {
|
||||||
// don't do anything if not running
|
// don't do anything if not running
|
||||||
if let Some(mut instance) = self.instance.take() {
|
if let Some(mut instance) = self.instance.take() {
|
||||||
info!(self.log, "Shutting down XWayland.");
|
info!(self.log, "Shutting down XWayland.");
|
||||||
self.wm.xwayland_exited();
|
|
||||||
// kill the client
|
// kill the client
|
||||||
instance.wayland_client.kill();
|
if let Some(client) = instance.wayland_client {
|
||||||
|
client.kill();
|
||||||
|
}
|
||||||
// remove the event source
|
// remove the event source
|
||||||
if let Some(s) = instance.startup_handler.take() {
|
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
|
// All connections and lockfiles are cleaned by their destructors
|
||||||
|
|
||||||
// Remove DISPLAY from the env
|
// 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) {
|
fn client_destroy<Data: 'static>(map: &::wayland_server::UserDataMap) {
|
||||||
let inner = map.get::<Rc<RefCell<Inner<WM>>>>().unwrap();
|
let inner = map.get::<Rc<RefCell<Inner<Data>>>>().unwrap();
|
||||||
|
|
||||||
// shutdown the server
|
|
||||||
let started_at = inner.borrow().instance.as_ref().map(|i| i.started_at);
|
|
||||||
inner.borrow_mut().shutdown();
|
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 mut guard = inner.borrow_mut();
|
||||||
let inner = &mut *guard;
|
let guard = &mut *guard;
|
||||||
// instance should never be None at this point
|
// instance should never be None at this point
|
||||||
let instance = inner.instance.as_mut().unwrap();
|
let instance = guard.instance.as_mut().unwrap();
|
||||||
let wm = &mut inner.wm;
|
|
||||||
// neither the child_stdout
|
// neither the child_stdout
|
||||||
let child_stdout = instance.child_stdout.as_mut().unwrap();
|
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) {
|
let success = match child_stdout.read(&mut buffer) {
|
||||||
Ok(len) => len > 0 && buffer[0] == b'S',
|
Ok(len) => len > 0 && buffer[0] == b'S',
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(inner.log, "Checking launch status failed"; "err" => format!("{:?}", e));
|
error!(guard.log, "Checking launch status failed"; "err" => format!("{:?}", e));
|
||||||
false
|
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()));
|
::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display()));
|
||||||
|
|
||||||
// signal the WM
|
// signal the WM
|
||||||
info!(inner.log, "XWayland is ready, signaling the WM.");
|
info!(guard.log, "XWayland is ready, signaling the WM.");
|
||||||
wm.xwayland_ready(
|
// send error occurs if the user dropped the channel... We cannot do much except ignore.
|
||||||
instance.wm_fd.take().unwrap(), // This is a bug if None
|
let _ = guard.sender.send(XWaylandEvent::Ready {
|
||||||
instance.wayland_client.clone(),
|
connection: instance.wm_fd.take().unwrap(), // This is a bug if None
|
||||||
);
|
client: instance.wayland_client.clone().unwrap(),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
error!(
|
error!(
|
||||||
inner.log,
|
guard.log,
|
||||||
"XWayland crashed at startup, will not try to restart it."
|
"XWayland crashed at startup, will not try to restart it."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// in all cases, cleanup
|
// in all cases, cleanup
|
||||||
if let Some(s) = instance.startup_handler.take() {
|
if let Some(s) = instance.startup_handler.take() {
|
||||||
(inner.kill_source)(s);
|
guard.handle.kill(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue