Merge pull request #89 from Smithay/xwayland
xwayland: core infrastructure
This commit is contained in:
commit
582e8de316
|
@ -42,6 +42,7 @@ env:
|
|||
- FEATURES="backend_session_udev"
|
||||
- FEATURES="backend_session_logind"
|
||||
- FEATURES="renderer_glium"
|
||||
- FEATURES="xwayland"
|
||||
# test default features
|
||||
- FEATURES="default"
|
||||
# test all features simultaneously
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -7,15 +7,15 @@ description = "Smithay is a library for writing wayland compositors."
|
|||
repository = "https://github.com/Smithay/smithay"
|
||||
|
||||
[dependencies]
|
||||
wayland-server = "0.20.1"
|
||||
wayland-sys = "0.20.1"
|
||||
wayland-server = "0.20.2"
|
||||
wayland-sys = "0.20.2"
|
||||
nix = "0.10.0"
|
||||
xkbcommon = "0.2.1"
|
||||
tempfile = "2.1.5"
|
||||
slog = { version = "2.1.1" }
|
||||
slog-stdlog = "3.0.2"
|
||||
libloading = "0.4.0"
|
||||
wayland-client = { version = "0.20.1", optional = true }
|
||||
wayland-client = { version = "0.20.2", optional = true }
|
||||
winit = { version = "0.10.0", optional = true }
|
||||
drm = { version = "^0.3.1", optional = true }
|
||||
gbm = { version = "^0.4.0", optional = true, default-features = false, features = ["drm-support"] }
|
||||
|
@ -24,7 +24,7 @@ input = { version = "0.4.0", optional = true }
|
|||
udev = { version = "0.2.0", optional = true }
|
||||
dbus = { version = "0.6.1", optional = true }
|
||||
systemd = { version = "^0.2.0", optional = true }
|
||||
wayland-protocols = { version = "0.20.1", features = ["unstable_protocols", "server"] }
|
||||
wayland-protocols = { version = "0.20.2", features = ["unstable_protocols", "server"] }
|
||||
image = "0.17.0"
|
||||
error-chain = "0.11.0"
|
||||
lazy_static = "1.0.0"
|
||||
|
@ -38,7 +38,7 @@ slog-async = "2.2"
|
|||
rand = "0.3"
|
||||
|
||||
[features]
|
||||
default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium"]
|
||||
default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium", "xwayland"]
|
||||
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
|
||||
backend_drm = ["drm", "gbm"]
|
||||
backend_libinput = ["input"]
|
||||
|
@ -47,4 +47,4 @@ backend_session_udev = ["udev", "backend_session"]
|
|||
backend_session_logind = ["dbus", "systemd", "backend_session"]
|
||||
backend_udev = ["udev", "backend_drm", "backend_session_udev"]
|
||||
renderer_glium = ["glium"]
|
||||
|
||||
xwayland = []
|
||||
|
|
|
@ -52,6 +52,9 @@ pub mod backend;
|
|||
pub mod wayland;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(feature = "xwayland")]
|
||||
pub mod xwayland;
|
||||
|
||||
fn slog_or_stdlog<L>(logger: L) -> ::slog::Logger
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
mod xserver;
|
||||
mod x11_sockets;
|
||||
|
||||
pub use self::xserver::{XWayland, XWindowManager};
|
|
@ -0,0 +1,137 @@
|
|||
use std::io::{Read, Write};
|
||||
use std::os::unix::io::FromRawFd;
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
use nix::{Error as NixError, Result as NixResult};
|
||||
use nix::errno::Errno;
|
||||
use nix::sys::socket;
|
||||
|
||||
/// Find a free X11 display slot and setup
|
||||
pub(crate) fn prepare_x11_sockets(log: ::slog::Logger) -> Result<(X11Lock, [UnixStream; 2]), ()> {
|
||||
for d in 0..33 {
|
||||
// if fails, try the next one
|
||||
if let Ok(lock) = X11Lock::grab(d, log.clone()) {
|
||||
// we got a lockfile, try and create the socket
|
||||
if let Ok(sockets) = open_x11_sockets_for_display(d) {
|
||||
return Ok((lock, sockets));
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we reach here, all values from 0 to 32 failed
|
||||
// we need to stop trying at some point
|
||||
return Err(());
|
||||
}
|
||||
|
||||
pub(crate) struct X11Lock {
|
||||
display: u32,
|
||||
log: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl X11Lock {
|
||||
/// Try to grab a lockfile for given X display number
|
||||
fn grab(display: u32, log: ::slog::Logger) -> Result<X11Lock, ()> {
|
||||
debug!(log, "Attempting to aquire an X11 display lock"; "D" => display);
|
||||
let filename = format!("/tmp/.X{}-lock", display);
|
||||
let lockfile = ::std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(&filename);
|
||||
match lockfile {
|
||||
Ok(mut file) => {
|
||||
// we got it, write our PID in it and we're good
|
||||
let ret = file.write_fmt(format_args!("{:>10}", ::nix::unistd::Pid::this()));
|
||||
if let Err(_) = ret {
|
||||
// write to the file failed ? we abandon
|
||||
::std::mem::drop(file);
|
||||
let _ = ::std::fs::remove_file(&filename);
|
||||
return Err(());
|
||||
} else {
|
||||
debug!(log, "X11 lock aquired"; "D" => display);
|
||||
// we got the lockfile and wrote our pid to it, all is good
|
||||
return Ok(X11Lock { display, log });
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
debug!(log, "Failed to acquire lock"; "D" => display);
|
||||
// we could not open the file, now we try to read it
|
||||
// and if it contains the pid of a process that no longer
|
||||
// exist (so if a previous x server claimed it and did not
|
||||
// exit gracefully and remove it), we claim it
|
||||
// if we can't open it, give up
|
||||
let mut file = ::std::fs::File::open(&filename).map_err(|_| ())?;
|
||||
let mut spid = [0u8; 11];
|
||||
file.read_exact(&mut spid).map_err(|_| ())?;
|
||||
::std::mem::drop(file);
|
||||
let pid = ::nix::unistd::Pid::from_raw(::std::str::from_utf8(&spid)
|
||||
.map_err(|_| ())?
|
||||
.trim()
|
||||
.parse::<i32>()
|
||||
.map_err(|_| ())?);
|
||||
if let Err(NixError::Sys(Errno::ESRCH)) = ::nix::sys::signal::kill(pid, None) {
|
||||
// no process whose pid equals the contents of the lockfile exists
|
||||
// remove the lockfile and try grabbing it again
|
||||
if let Ok(()) = ::std::fs::remove_file(filename) {
|
||||
debug!(log, "Lock was blocked by a defunct X11 server, trying again"; "D" => display);
|
||||
return X11Lock::grab(display, log);
|
||||
} else {
|
||||
// we could not remove the lockfile, abort
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
// if we reach here, this lockfile exists and is probably in use, give up
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn display(&self) -> u32 {
|
||||
self.display
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for X11Lock {
|
||||
fn drop(&mut self) {
|
||||
info!(self.log, "Cleaning up X11 lock.");
|
||||
// Cleanup all the X11 files
|
||||
if let Err(e) = ::std::fs::remove_file(format!("/tmp/.X11-unix/X{}", self.display)) {
|
||||
warn!(self.log, "Failed to remove X11 socket"; "error" => format!("{:?}", e));
|
||||
}
|
||||
if let Err(e) = ::std::fs::remove_file(format!("/tmp/.X{}-lock", self.display)) {
|
||||
warn!(self.log, "Failed to remove X11 lockfile"; "error" => format!("{:?}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the two unix sockets an X server listens on
|
||||
///
|
||||
/// Should only be done after the associated lockfile is aquired!
|
||||
fn open_x11_sockets_for_display(display: u32) -> NixResult<[UnixStream; 2]> {
|
||||
let path = format!("/tmp/.X11-unix/X{}", display);
|
||||
// We know this path is not to long, these unwrap cannot fail
|
||||
let fs_addr = socket::UnixAddr::new(path.as_bytes()).unwrap();
|
||||
let abs_addr = socket::UnixAddr::new_abstract(path.as_bytes()).unwrap();
|
||||
let fs_socket = open_socket(fs_addr)?;
|
||||
let abstract_socket = open_socket(abs_addr)?;
|
||||
Ok([fs_socket, abstract_socket])
|
||||
}
|
||||
|
||||
/// Open an unix socket for listening and bind it to given path
|
||||
fn open_socket(addr: socket::UnixAddr) -> NixResult<UnixStream> {
|
||||
// create an unix stream socket
|
||||
let fd = socket::socket(
|
||||
socket::AddressFamily::Unix,
|
||||
socket::SockType::Stream,
|
||||
socket::SockFlag::SOCK_CLOEXEC,
|
||||
None,
|
||||
)?;
|
||||
// bind it to requested address
|
||||
if let Err(e) = socket::bind(fd, &socket::SockAddr::Unix(addr)) {
|
||||
let _ = ::nix::unistd::close(fd);
|
||||
return Err(e);
|
||||
}
|
||||
if let Err(e) = socket::listen(fd, 1) {
|
||||
let _ = ::nix::unistd::close(fd);
|
||||
return Err(e);
|
||||
}
|
||||
Ok(unsafe { FromRawFd::from_raw_fd(fd) })
|
||||
}
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* 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 ouserlves 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 respectly:
|
||||
* - /tmp/.X11-unix/X$D
|
||||
* - @/tmp/.X11-unix/X$D (abstract socket)
|
||||
*
|
||||
* The XWayland server is spawned via fork+exec.
|
||||
* -> wlroot does a double-fork while weston a single one, why ??
|
||||
* -> https://stackoverflow.com/questions/881388/
|
||||
* -> once it is started, it sends us a SIGUSR1, we need to setup a listener
|
||||
* for it and when we receive it we can launch the WM
|
||||
* -> we need to track if the Xwayland crashes, to restart it
|
||||
*
|
||||
* cf https://github.com/swaywm/wlroots/blob/master/xwayland/xwayland.c
|
||||
*
|
||||
*/
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::env;
|
||||
use std::ffi::CString;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
use nix::{Error as NixError, Result as NixResult};
|
||||
use nix::errno::Errno;
|
||||
use nix::unistd::{fork, ForkResult, Pid};
|
||||
use nix::sys::signal;
|
||||
|
||||
use wayland_server::{Client, Display, LoopToken};
|
||||
use wayland_server::sources::{SignalEvent, Source};
|
||||
|
||||
use super::x11_sockets::{X11Lock, prepare_x11_sockets};
|
||||
|
||||
/// 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 previlegied 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>(
|
||||
wm: WM,
|
||||
token: LoopToken,
|
||||
display: Rc<RefCell<Display>>,
|
||||
logger: L,
|
||||
) -> Result<XWayland<WM>, ()>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = ::slog_or_stdlog(logger);
|
||||
let inner = Rc::new(RefCell::new(Inner {
|
||||
wm,
|
||||
token,
|
||||
wayland_display: display,
|
||||
instance: None,
|
||||
log: log.new(o!("smithay_module" => "XWayland")),
|
||||
}));
|
||||
launch(&inner)?;
|
||||
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,
|
||||
sigusr1_handler: Option<Source<SignalEvent>>,
|
||||
wm_fd: Option<UnixStream>,
|
||||
started_at: ::std::time::Instant,
|
||||
child_pid: Option<Pid>,
|
||||
}
|
||||
|
||||
// Inner implementation of the XWayland manager
|
||||
struct Inner<WM: XWindowManager> {
|
||||
wm: WM,
|
||||
token: LoopToken,
|
||||
wayland_display: Rc<RefCell<Display>>,
|
||||
instance: Option<XWaylandInstance>,
|
||||
log: ::slog::Logger,
|
||||
}
|
||||
|
||||
// Launch an XWayland server
|
||||
//
|
||||
// Does nothing if there is already a launched instance
|
||||
fn launch<WM: XWindowManager + 'static>(inner: &Rc<RefCell<Inner<WM>>>) -> Result<(), ()> {
|
||||
let mut guard = inner.borrow_mut();
|
||||
if guard.instance.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
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())
|
||||
};
|
||||
client.set_user_data(Rc::into_raw(inner.clone()) as *const () as *mut ());
|
||||
client.set_destructor(client_destroy::<WM>);
|
||||
|
||||
// setup the SIGUSR1 handler
|
||||
let my_inner = inner.clone();
|
||||
let sigusr1_handler = guard
|
||||
.token
|
||||
.add_signal_event_source(signal::Signal::SIGUSR1, move |_, ()| {
|
||||
xwayland_ready(&my_inner)
|
||||
})
|
||||
.map_err(|_| ())?;
|
||||
|
||||
// all is ready, we can do the fork dance
|
||||
let child_pid = match fork() {
|
||||
Ok(ForkResult::Parent { child }) => {
|
||||
// we are the main smithay process
|
||||
child
|
||||
}
|
||||
Ok(ForkResult::Child) => {
|
||||
// we are the first child
|
||||
let ppid = Pid::parent();
|
||||
let mut set = signal::SigSet::empty();
|
||||
set.add(signal::Signal::SIGUSR1);
|
||||
set.add(signal::Signal::SIGCHLD);
|
||||
// we can't handle errors here anyway
|
||||
let _ = signal::sigprocmask(signal::SigmaskHow::SIG_BLOCK, Some(&set), None);
|
||||
match fork() {
|
||||
Ok(ForkResult::Parent { child }) => {
|
||||
// we are still the first child
|
||||
let sig = set.wait();
|
||||
// send USR1 to parent
|
||||
let _ = signal::kill(ppid, signal::Signal::SIGUSR1);
|
||||
// Parent will wait for us and know from out
|
||||
// exit status if XWayland launch was a success or not =)
|
||||
if let Ok(signal::Signal::SIGCHLD) = sig {
|
||||
// Xwayland has exited before being ready
|
||||
let _ = ::nix::sys::wait::waitpid(child, None);
|
||||
unsafe { ::nix::libc::exit(1) };
|
||||
}
|
||||
unsafe { ::nix::libc::exit(0) };
|
||||
}
|
||||
Ok(ForkResult::Child) => {
|
||||
// we are the second child, we exec xwayland
|
||||
match exec_xwayland(lock.display(), wl_x11, x_wm_x11, &x_fds) {
|
||||
Ok(x) => match x {},
|
||||
Err(e) => {
|
||||
// well, what can we do ?
|
||||
error!(guard.log, "exec XWayland failed"; "err" => format!("{:?}", e));
|
||||
unsafe { ::nix::libc::exit(1) };
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
// well, what can we do ?
|
||||
error!(guard.log, "XWayland second fork failed"; "err" => format!("{:?}", e));
|
||||
unsafe { ::nix::libc::exit(1) };
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!(guard.log, "XWayland first fork failed"; "err" => format!("{:?}", e));
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
guard.instance = Some(XWaylandInstance {
|
||||
display_lock: lock,
|
||||
wayland_client: client,
|
||||
sigusr1_handler: Some(sigusr1_handler),
|
||||
wm_fd: Some(x_wm_me),
|
||||
started_at: creation_time,
|
||||
child_pid: Some(child_pid),
|
||||
});
|
||||
|
||||
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.sigusr1_handler.take() {
|
||||
s.remove();
|
||||
}
|
||||
// All connexions and lockfiles are cleaned by their destructors
|
||||
|
||||
// Remove DISPLAY from the env
|
||||
::std::env::remove_var("DISPLAY");
|
||||
// We do like wlroots:
|
||||
// > We do not kill the Xwayland process, it dies to broken pipe
|
||||
// > after we close our side of the wm/wl fds. This is more reliable
|
||||
// > than trying to kill something that might no longer be Xwayland.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn client_destroy<WM: XWindowManager + 'static>(data: *mut ()) {
|
||||
let inner = unsafe { Rc::from_raw(data as *const () as *const RefCell<Inner<WM>>) };
|
||||
|
||||
// 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);
|
||||
} 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>>>) {
|
||||
use nix::sys::wait;
|
||||
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 pid
|
||||
let pid = instance.child_pid.unwrap();
|
||||
|
||||
// find out if the launch was a success by waiting on the intermediate child
|
||||
let success: bool;
|
||||
loop {
|
||||
match wait::waitpid(pid, None) {
|
||||
Ok(wait::WaitStatus::Exited(_, 0)) => {
|
||||
// XWayland was correctly started :)
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
Err(NixError::Sys(Errno::EINTR)) => {
|
||||
// interupted, retry
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
// something went wrong :(
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if success {
|
||||
// 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(),
|
||||
);
|
||||
|
||||
// setup the environemnt
|
||||
::std::env::set_var("DISPLAY", format!(":{}", instance.display_lock.display()));
|
||||
} else {
|
||||
error!(
|
||||
inner.log,
|
||||
"XWayland crashed at startup, will not try to restart it."
|
||||
);
|
||||
}
|
||||
|
||||
// in all cases, cleanup
|
||||
if let Some(s) = instance.sigusr1_handler.take() {
|
||||
s.remove();
|
||||
}
|
||||
}
|
||||
|
||||
enum Void {}
|
||||
|
||||
/// Exec xwayland with given sockets on given display
|
||||
///
|
||||
/// If this returns, that means that something failed
|
||||
fn exec_xwayland(
|
||||
display: u32,
|
||||
wayland_socket: UnixStream,
|
||||
wm_socket: UnixStream,
|
||||
listen_sockets: &[UnixStream],
|
||||
) -> NixResult<Void> {
|
||||
// uset the CLOEXEC flag from the sockets we need to pass
|
||||
// to xwayland
|
||||
unset_cloexec(&wayland_socket)?;
|
||||
unset_cloexec(&wm_socket)?;
|
||||
for socket in listen_sockets {
|
||||
unset_cloexec(socket)?;
|
||||
}
|
||||
// prepare the arguments to Xwayland
|
||||
let mut args = vec![
|
||||
CString::new("Xwayland").unwrap(),
|
||||
CString::new(format!(":{}", display)).unwrap(),
|
||||
CString::new("-rootless").unwrap(),
|
||||
CString::new("-terminate").unwrap(),
|
||||
CString::new("-wm").unwrap(),
|
||||
CString::new(format!("{}", wm_socket.as_raw_fd())).unwrap(),
|
||||
];
|
||||
for socket in listen_sockets {
|
||||
args.push(CString::new("-listen").unwrap());
|
||||
args.push(CString::new(format!("{}", socket.as_raw_fd())).unwrap());
|
||||
}
|
||||
// setup the environment: clear everything except PATH and XDG_RUNTIME_DIR
|
||||
for (key, _) in env::vars_os() {
|
||||
if key.to_str() == Some("PATH") || key.to_str() == Some("XDG_RUNTIME_DIR") {
|
||||
continue;
|
||||
}
|
||||
env::remove_var(key);
|
||||
}
|
||||
// the WAYLAND_SOCKET var tells Xwayland where to connect as a wayland client
|
||||
env::set_var("WAYLAND_SOCKET", format!("{}", wayland_socket.as_raw_fd()));
|
||||
|
||||
// ignore SIGUSR1, this will make the Xwayland server send us this
|
||||
// signal when it is ready apparently
|
||||
unsafe {
|
||||
use nix::sys::signal::*;
|
||||
sigaction(
|
||||
Signal::SIGUSR1,
|
||||
&SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty()),
|
||||
)?;
|
||||
}
|
||||
|
||||
// run it
|
||||
let ret = ::nix::unistd::execvp(&CString::new("Xwayland").unwrap(), &args)?;
|
||||
// small dance to actually return Void
|
||||
match ret {}
|
||||
}
|
||||
|
||||
/// 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<F: AsRawFd>(fd: &F) -> NixResult<()> {
|
||||
use nix::fcntl::{fcntl, FcntlArg, FdFlag};
|
||||
fcntl(fd.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty()))?;
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue