1
0
Fork 0
forked from wry/wry

autocommit 2022-02-24 16:30:11 CET

This commit is contained in:
Julian Orth 2022-02-24 16:30:11 +01:00
parent 666e475032
commit 7d28d30666
39 changed files with 1670 additions and 209 deletions

View file

@ -1,20 +1,27 @@
use crate::client::ClientError;
use crate::event_loop::{EventLoopDispatcher, EventLoopError, EventLoopId};
use crate::state::State;
use crate::ErrorFmt;
use std::rc::Rc;
use thiserror::Error;
use uapi::{c, Errno, OwnedFd};
use uapi::{c, format_ustr, Errno, OwnedFd, Ustring};
#[derive(Debug, Error)]
pub enum AcceptorError {
#[error("XDG_RUNTIME_DIR is not set")]
XrdNotSet,
#[error("XDG_RUNTIME_DIR is too long to form a unix socket address")]
XrdTooLong,
#[error("XDG_RUNTIME_DIR ({0:?}) is too long to form a unix socket address")]
XrdTooLong(String),
#[error("Could not create a wayland socket")]
SocketFailed(#[source] std::io::Error),
#[error("Could not stat the existing socket")]
SocketStat(#[source] std::io::Error),
#[error("Could not start listening for incoming connections")]
ListenFailed(#[source] std::io::Error),
#[error("Could not open the lock file")]
OpenLockFile(#[source] std::io::Error),
#[error("Could not lock the lock file")]
LockLockFile(#[source] std::io::Error),
#[error("The wayland socket is in an error state")]
ErrorEvent,
#[error("Could not accept new connections")]
@ -30,76 +37,110 @@ pub enum AcceptorError {
}
pub struct Acceptor {
_unlinker: Unlinker,
id: EventLoopId,
fd: OwnedFd,
socket: AllocatedSocket,
global: Rc<State>,
}
struct Unlinker(String);
struct AllocatedSocket {
// wayland-x
name: Ustring,
// /run/user/1000/wayland-x
path: Ustring,
fd: Rc<OwnedFd>,
// /run/user/1000/wayland-x.lock
lock_path: Ustring,
_lock_fd: OwnedFd,
}
impl Drop for Unlinker {
impl Drop for AllocatedSocket {
fn drop(&mut self) {
let _ = uapi::unlink(self.0.as_str());
let _ = uapi::unlink(&self.path);
let _ = uapi::unlink(&self.lock_path);
}
}
fn socket_path(xrd: &str, id: u32) -> String {
format!("{}/wayland-{}", xrd, id)
}
fn bind_socket(fd: i32, xdr: &str) -> Result<u32, AcceptorError> {
fn bind_socket(fd: &Rc<OwnedFd>, xrd: &str, id: u32) -> Result<AllocatedSocket, AcceptorError> {
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
addr.sun_family = c::AF_UNIX as _;
for i in 0..1000 {
let path = socket_path(xdr, i);
if path.len() + 1 > addr.sun_path.len() {
return Err(AcceptorError::XrdTooLong);
let name = format_ustr!("wayland-{}", id);
let path = format_ustr!("{}/{}", xrd, name.display());
let lock_path = format_ustr!("{}.lock", path.display());
if path.len() + 1 > addr.sun_path.len() {
return Err(AcceptorError::XrdTooLong(xrd.to_string()));
}
let lock_fd = match uapi::open(&*lock_path, c::O_CREAT | c::O_CLOEXEC | c::O_RDWR, 0o644) {
Ok(l) => l,
Err(e) => return Err(AcceptorError::OpenLockFile(e.into())),
};
if let Err(e) = uapi::flock(lock_fd.raw(), c::LOCK_EX | c::LOCK_NB) {
return Err(AcceptorError::LockLockFile(e.into()));
}
match uapi::lstat(&*path) {
Ok(_) => {
log::info!("Unlinking {}", path.display());
let _ = uapi::unlink(&*path);
}
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
sun_path[..path.len()].copy_from_slice(path.as_bytes());
sun_path[path.len()] = 0;
match uapi::bind(fd, &addr) {
Ok(()) => return Ok(i),
Err(Errno(c::EADDRINUSE)) => {
log::warn!("Socket {} is already in use", path);
Err(Errno(c::ENOENT)) => {}
Err(e) => return Err(AcceptorError::SocketStat(e.into())),
}
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
sun_path[..path.len()].copy_from_slice(path.as_bytes());
sun_path[path.len()] = 0;
if let Err(e) = uapi::bind(fd.raw(), &addr) {
return Err(AcceptorError::BindFailed(e.into()));
}
Ok(AllocatedSocket {
name,
path,
fd: fd.clone(),
lock_path,
_lock_fd: lock_fd,
})
}
fn allocate_socket() -> Result<AllocatedSocket, AcceptorError> {
let xrd = match std::env::var("XDG_RUNTIME_DIR") {
Ok(d) => d,
Err(_) => return Err(AcceptorError::XrdNotSet),
};
let fd = match uapi::socket(
c::AF_UNIX,
c::SOCK_STREAM | c::SOCK_NONBLOCK | c::SOCK_CLOEXEC,
0,
) {
Ok(f) => Rc::new(f),
Err(e) => return Err(AcceptorError::SocketFailed(e.into())),
};
for i in 1..1000 {
match bind_socket(&fd, &xrd, i) {
Ok(s) => return Ok(s),
Err(e) => {
log::warn!("Cannot use the wayland-{} socket: {}", i, ErrorFmt(e));
}
Err(e) => return Err(AcceptorError::BindFailed(e.into())),
}
}
Err(AcceptorError::AddressesInUse)
}
impl Acceptor {
pub fn install(global: &Rc<State>) -> Result<String, AcceptorError> {
let xrd = match std::env::var("XDG_RUNTIME_DIR") {
Ok(d) => d,
Err(_) => return Err(AcceptorError::XrdNotSet),
};
let fd = match uapi::socket(
c::AF_UNIX,
c::SOCK_STREAM | c::SOCK_NONBLOCK | c::SOCK_CLOEXEC,
0,
) {
Ok(f) => f,
Err(e) => return Err(AcceptorError::SocketFailed(e.into())),
};
let socket_id = bind_socket(fd.raw(), &xrd)?;
let socket_path = socket_path(&xrd, socket_id);
log::info!("bound to socket {}", socket_path);
let unlinker = Unlinker(socket_path.clone());
if let Err(e) = uapi::listen(fd.raw(), 4096) {
pub fn install(global: &Rc<State>) -> Result<Ustring, AcceptorError> {
let socket = allocate_socket()?;
log::info!("bound to socket {}", socket.path.display());
if let Err(e) = uapi::listen(socket.fd.raw(), 4096) {
return Err(AcceptorError::ListenFailed(e.into()));
}
let id = global.el.id();
let name = socket.name.to_owned();
let acc = Rc::new(Acceptor {
_unlinker: unlinker,
id,
fd,
socket,
global: global.clone(),
});
global.el.insert(id, Some(acc.fd.raw()), c::EPOLLIN, acc)?;
Ok(socket_path)
global
.el
.insert(id, Some(acc.socket.fd.raw()), c::EPOLLIN, acc)?;
Ok(name)
}
}
@ -110,7 +151,7 @@ impl EventLoopDispatcher for Acceptor {
}
loop {
let fd = match uapi::accept4(
self.fd.raw(),
self.socket.fd.raw(),
uapi::sockaddr_none_mut(),
c::SOCK_NONBLOCK | c::SOCK_CLOEXEC,
) {

View file

@ -22,6 +22,7 @@ use std::mem;
use std::ops::DerefMut;
use std::rc::Rc;
use uapi::{c, OwnedFd};
use crate::xwayland::XWaylandEvent;
mod error;
mod objects;
@ -92,6 +93,19 @@ impl Clients {
}
}
};
self.spawn2(id, global, socket, uid, pid, None)?;
Ok(())
}
pub fn spawn2(
&self,
id: ClientId,
global: &Rc<State>,
socket: OwnedFd,
uid: c::uid_t,
pid: c::pid_t,
xwayland_queue: Option<Rc<AsyncQueue<XWaylandEvent>>>,
) -> Result<Rc<Client>, ClientError> {
let data = Rc::new(Client {
id,
state: global.clone(),
@ -103,6 +117,7 @@ impl Clients {
shutdown: Default::default(),
dispatch_frame_requests: AsyncQueue::new(),
tracker: Default::default(),
xwayland_queue,
});
track!(data, data);
let display = Rc::new(WlDisplay::new(&data));
@ -111,7 +126,7 @@ impl Clients {
data.objects.add_client_object(display).expect("");
let client = ClientHolder {
_handler: global.eng.spawn(tasks::client(data.clone())),
data,
data: data.clone(),
};
log::info!(
"Client {} connected, pid: {}, uid: {}, fd: {}",
@ -121,7 +136,7 @@ impl Clients {
client.data.socket.raw()
);
self.clients.borrow_mut().insert(client.data.id, client);
Ok(())
Ok(data)
}
pub fn kill(&self, client: ClientId) {
@ -193,6 +208,7 @@ pub struct Client {
shutdown: AsyncEvent,
pub dispatch_frame_requests: AsyncQueue<Rc<WlCallback>>,
pub tracker: Tracker<Client>,
pub xwayland_queue: Option<Rc<AsyncQueue<XWaylandEvent>>>,
}
const MAX_PENDING_BUFFERS: usize = 10;

View file

@ -310,7 +310,7 @@ impl ConfigProxyHandler {
Some(f) => f,
_ => return Err(RunError::NoForker),
};
forker.spawn(prog.to_string(), args, env);
forker.spawn(prog.to_string(), args, env, None);
Ok(())
}

View file

@ -3,23 +3,24 @@ mod io;
use crate::async_engine::{AsyncFd, SpawnedFuture};
use crate::forker::clone3::{fork_with_pidfd, Forked};
use crate::utils::buffd::{BufFdError};
use crate::forker::io::{IoIn, IoOut};
use crate::utils::buffd::BufFdError;
use crate::utils::copyhashmap::CopyHashMap;
use crate::{AsyncEngine, AsyncQueue, ErrorFmt, EventLoop, State, Wheel};
use crate::{xwayland, AsyncEngine, AsyncQueue, ErrorFmt, EventLoop, NumCell, State, Wheel};
use bincode::error::{DecodeError, EncodeError};
use bincode::{Decode, Encode};
use i4config::_private::bincode_ops;
use log::Level;
use std::cell::Cell;
use std::cell::{Cell, RefCell};
use std::env;
use std::ffi::OsStr;
use std::io::Read;
use std::io::Write;
use std::os::unix::ffi::OsStrExt;
use std::rc::Rc;
use bincode::error::{DecodeError, EncodeError};
use std::rc::{Rc, Weak};
use std::task::{Poll, Waker};
use thiserror::Error;
use uapi::{c, pipe2, Fd, IntoUstr, OwnedFd, UstrPtr};
use crate::forker::io::{IoIn, IoOut};
use uapi::{c, pipe2, Errno, Fd, IntoUstr, OwnedFd, UstrPtr};
pub struct ForkerProxy {
pidfd: Rc<OwnedFd>,
@ -29,6 +30,14 @@ pub struct ForkerProxy {
task_out: Cell<Option<SpawnedFuture<()>>>,
task_proc: Cell<Option<SpawnedFuture<()>>>,
outgoing: AsyncQueue<ServerMessage>,
next_id: NumCell<u32>,
pending_pidfds: CopyHashMap<u32, Weak<PidfdHandoff>>,
fds: RefCell<Vec<Rc<OwnedFd>>>,
}
struct PidfdHandoff {
pidfd: Cell<Option<Result<OwnedFd, ForkerError>>>,
waiter: Cell<Option<Waker>>,
}
#[derive(Debug, Error)]
@ -45,6 +54,8 @@ pub enum ForkerError {
DecodeFailed(#[source] DecodeError),
#[error("Could not encode the next message")]
EncodeFailed(#[source] EncodeError),
#[error("Could not fork")]
PidfdForkFailed,
}
impl ForkerProxy {
@ -67,6 +78,9 @@ impl ForkerProxy {
task_out: Cell::new(None),
task_proc: Cell::new(None),
outgoing: Default::default(),
next_id: Default::default(),
pending_pidfds: Default::default(),
fds: Default::default(),
}),
Forked::Child { .. } => Forker::handle(pid, child),
}
@ -90,12 +104,65 @@ impl ForkerProxy {
pub fn setenv(&self, key: &[u8], val: &[u8]) {
self.outgoing.push(ServerMessage::SetEnv {
var: key.to_vec(),
val: val.to_vec(),
val: Some(val.to_vec()),
})
}
pub fn spawn(&self, prog: String, args: Vec<String>, env: Vec<(String, String)>) {
self.outgoing.push(ServerMessage::Spawn { prog, args, env })
pub fn unsetenv(&self, key: &[u8]) {
self.outgoing.push(ServerMessage::SetEnv {
var: key.to_vec(),
val: None,
})
}
async fn pidfd(&self, id: u32) -> Result<OwnedFd, ForkerError> {
let handoff = Rc::new(PidfdHandoff {
pidfd: Cell::new(None),
waiter: Cell::new(None),
});
self.pending_pidfds.set(id, Rc::downgrade(&handoff));
futures::future::poll_fn(|ctx| {
if let Some(pidfd) = handoff.pidfd.take() {
Poll::Ready(pidfd)
} else {
handoff.waiter.set(Some(ctx.waker().clone()));
Poll::Pending
}
})
.await
}
pub async fn xwayland(
&self,
stderr: Rc<OwnedFd>,
dfd: Rc<OwnedFd>,
listenfd: Rc<OwnedFd>,
wmfd: Rc<OwnedFd>,
waylandfd: Rc<OwnedFd>,
) -> Result<OwnedFd, ForkerError> {
self.fds.borrow_mut().extend([stderr, dfd, listenfd, wmfd, waylandfd]);
let id = self.next_id.fetch_add(1);
self.outgoing.push(ServerMessage::Xwayland { id });
self.pidfd(id).await
}
pub fn spawn(
&self,
prog: String,
args: Vec<String>,
env: Vec<(String, String)>,
stderr: Option<Rc<OwnedFd>>,
) {
let have_stderr = stderr.is_some();
if let Some(stderr) = stderr {
self.fds.borrow_mut().push(stderr);
}
self.outgoing.push(ServerMessage::Spawn {
prog,
args,
env,
stderr: have_stderr,
})
}
async fn incoming(self: Rc<Self>, socket: AsyncFd) {
@ -109,13 +176,29 @@ impl ForkerProxy {
return;
}
};
self.handle_msg(msg);
self.handle_msg(msg, &mut io);
}
}
fn handle_msg(&self, msg: ForkerMessage) {
fn handle_msg(&self, msg: ForkerMessage, io: &mut IoIn) {
match msg {
ForkerMessage::Log { level, msg } => self.handle_log(level, &msg),
ForkerMessage::PidFd { id, success } => self.handle_pidfd(id, success, io),
}
}
fn handle_pidfd(&self, id: u32, success: bool, io: &mut IoIn) {
let res = match success {
true => Ok(io.pop_fd().unwrap()),
_ => Err(ForkerError::PidfdForkFailed),
};
if let Some(handoff) = self.pending_pidfds.remove(&id) {
if let Some(handoff) = handoff.upgrade() {
handoff.pidfd.set(Some(res));
if let Some(w) = handoff.waiter.take() {
w.wake();
}
}
}
}
@ -135,6 +218,9 @@ impl ForkerProxy {
let mut io = IoOut::new(socket);
loop {
let msg = self.outgoing.pop().await;
for fd in self.fds.borrow_mut().drain(..) {
io.push_fd(fd);
}
if let Err(e) = io.write_msg(msg).await {
log::error!("Could not write to the ol' forker: {}", ErrorFmt(e));
state.forker.set(None);
@ -159,23 +245,29 @@ impl ForkerProxy {
enum ServerMessage {
SetEnv {
var: Vec<u8>,
val: Vec<u8>,
val: Option<Vec<u8>>,
},
Spawn {
prog: String,
args: Vec<String>,
env: Vec<(String, String)>,
stderr: bool,
},
Xwayland {
id: u32,
},
}
#[derive(Encode, Decode)]
enum ForkerMessage {
Log { level: usize, msg: String },
PidFd { id: u32, success: bool },
}
struct Forker {
socket: AsyncFd,
ae: Rc<AsyncEngine>,
fds: RefCell<Vec<Rc<OwnedFd>>>,
outgoing: AsyncQueue<ForkerMessage>,
pending_spawns: CopyHashMap<c::pid_t, SpawnedFuture<()>>,
}
@ -206,6 +298,7 @@ impl Forker {
let forker = Rc::new(Forker {
socket: ae.fd(&socket).unwrap(),
ae: ae.clone(),
fds: RefCell::new(vec![]),
outgoing: Default::default(),
pending_spawns: Default::default(),
});
@ -219,6 +312,9 @@ impl Forker {
let mut io = IoOut::new(self.socket.clone());
loop {
let msg = self.outgoing.pop().await;
for fd in self.fds.borrow_mut().drain(..) {
io.push_fd(fd);
}
io.write_msg(msg).await.unwrap();
}
}
@ -227,26 +323,76 @@ impl Forker {
let mut io = IoIn::new(self.socket.clone());
loop {
let msg = io.read_msg().await.unwrap();
self.handle_msg(msg);
self.handle_msg(msg, &mut io);
}
}
fn handle_msg(self: &Rc<Self>, msg: ServerMessage) {
fn handle_msg(self: &Rc<Self>, msg: ServerMessage, io: &mut IoIn) {
match msg {
ServerMessage::SetEnv { var, val } => self.handle_set_env(&var, &val),
ServerMessage::Spawn { prog, args, env } => self.handle_spawn(prog, args, env),
ServerMessage::SetEnv { var, val } => self.handle_set_env(&var, val),
ServerMessage::Spawn {
prog,
args,
env,
stderr,
} => self.handle_spawn(prog, args, env, stderr, io),
ServerMessage::Xwayland { id } => self.handle_xwayland(io, id),
}
}
fn handle_set_env(self: &Rc<Self>, var: &[u8], val: &[u8]) {
env::set_var(OsStr::from_bytes(var), OsStr::from_bytes(val));
fn handle_set_env(self: &Rc<Self>, var: &[u8], val: Option<Vec<u8>>) {
let var = OsStr::from_bytes(var);
match val {
Some(val) => env::set_var(var, OsStr::from_bytes(&val)),
_ => env::remove_var(var),
}
}
fn handle_spawn(self: &Rc<Self>, prog: String, args: Vec<String>, env: Vec<(String, String)>) {
fn handle_xwayland(self: &Rc<Self>, io: &mut IoIn, id: u32) {
let stderr = io.pop_fd();
let fds = vec![
io.pop_fd().unwrap(),
io.pop_fd().unwrap(),
io.pop_fd().unwrap(),
io.pop_fd().unwrap(),
];
let (prog, args) = xwayland::build_args(&fds);
let env = vec![("WAYLAND_SOCKET".to_string(), fds[3].raw().to_string())];
self.spawn(prog, args, env, stderr, fds, Some(id));
}
fn handle_spawn(
self: &Rc<Self>,
prog: String,
args: Vec<String>,
env: Vec<(String, String)>,
stderr: bool,
io: &mut IoIn,
) {
let stderr = match stderr {
true => io.pop_fd(),
_ => None,
};
self.spawn(prog, args, env, stderr, vec![], None)
}
fn spawn(
self: &Rc<Self>,
prog: String,
args: Vec<String>,
env: Vec<(String, String)>,
stderr: Option<OwnedFd>,
fds: Vec<OwnedFd>,
pidfd_id: Option<u32>,
) {
let (read, mut write) = pipe2(c::O_CLOEXEC).unwrap();
let res = match fork_with_pidfd(false) {
Ok(o) => o,
Err(e) => {
if let Some(id) = pidfd_id {
self.outgoing
.push(ForkerMessage::PidFd { id, success: false });
}
self.outgoing.push(ForkerMessage::Log {
level: log::Level::Error as usize,
msg: ErrorFmt(e).to_string(),
@ -255,7 +401,12 @@ impl Forker {
}
};
match res {
Forked::Parent { pid, .. } => {
Forked::Parent { pid, pidfd } => {
if let Some(id) = pidfd_id {
self.fds.borrow_mut().push(Rc::new(pidfd));
self.outgoing
.push(ForkerMessage::PidFd { id, success: true });
}
drop(write);
let slf = self.clone();
let spawn = self.ae.spawn(async move {
@ -274,20 +425,39 @@ impl Forker {
self.pending_spawns.set(pid, spawn);
}
Forked::Child { .. } => {
unsafe {
c::signal(c::SIGCHLD, c::SIG_DFL);
}
for (key, val) in env {
env::set_var(&key, &val);
}
let prog = prog.into_ustr();
let mut argsnt = UstrPtr::new();
argsnt.push(&prog);
for arg in args {
argsnt.push(arg);
}
if let Err(e) = uapi::execvp(&prog, &argsnt) {
let _ = write.write_all(std::io::Error::from(e).to_string().as_bytes());
let err = (|| {
if let Some(stderr) = stderr {
uapi::dup2(stderr.raw(), 2).unwrap();
}
for fd in fds {
let fd = fd.unwrap();
let res: Result<_, Errno> = (|| {
uapi::fcntl_setfd(fd, uapi::fcntl_getfd(fd)? & !c::FD_CLOEXEC)?;
Ok(())
})();
if let Err(e) = res {
return Err(SpawnError::Cloexec(e.into()));
}
}
unsafe {
c::signal(c::SIGCHLD, c::SIG_DFL);
}
for (key, val) in env {
env::set_var(&key, &val);
}
let prog = prog.into_ustr();
let mut argsnt = UstrPtr::new();
argsnt.push(&prog);
for arg in args {
argsnt.push(arg);
}
if let Err(e) = uapi::execvp(&prog, &argsnt) {
return Err(SpawnError::Exec(e.into()));
}
Ok(())
})();
if let Err(e) = err {
let _ = write.write_all(ErrorFmt(e).to_string().as_bytes());
}
std::process::exit(1);
}
@ -295,6 +465,14 @@ impl Forker {
}
}
#[derive(Debug, Error)]
enum SpawnError {
#[error("exec failed")]
Exec(#[source] std::io::Error),
#[error("Could not unset cloexec flag")]
Cloexec(#[source] std::io::Error),
}
fn setup_fds(mut socket: OwnedFd) -> OwnedFd {
if socket.raw() != 0 {
uapi::dup3(socket.unwrap(), 0, 0).unwrap();

View file

@ -1,13 +1,13 @@
use bincode::{Decode, Encode};
use std::mem;
use std::rc::Rc;
use bincode::{Decode, Encode};
use bincode::error::EncodeError;
use uapi::OwnedFd;
use i4config::_private::bincode_ops;
use crate::async_engine::AsyncFd;
use crate::ForkerError;
use crate::utils::buffd::{BufFdIn, BufFdOut};
use crate::utils::vec_ext::VecExt;
use crate::ForkerError;
use i4config::_private::bincode_ops;
use uapi::OwnedFd;
pub struct IoIn {
incoming: BufFdIn,
@ -18,7 +18,7 @@ impl IoIn {
pub fn new(fd: AsyncFd) -> Self {
Self {
incoming: BufFdIn::new(fd),
scratch: vec![]
scratch: vec![],
}
}
@ -51,7 +51,7 @@ impl IoIn {
pub struct IoOut {
outgoing: BufFdOut,
scratch: Vec<u8>,
fds: Vec<Rc<OwnedFd>>
fds: Vec<Rc<OwnedFd>>,
}
impl IoOut {
@ -59,7 +59,7 @@ impl IoOut {
Self {
outgoing: BufFdOut::new(fd),
scratch: vec![],
fds: vec![]
fds: vec![],
}
}

View file

@ -7,6 +7,7 @@ use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_registry::WlRegistry;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::zwlr_layer_shell_v1::ZwlrLayerShellV1Global;
use crate::ifs::zxdg_output_manager_v1::ZxdgOutputManagerV1Global;
use crate::object::{Interface, ObjectId};
use crate::utils::copyhashmap::CopyHashMap;
use crate::{
@ -19,7 +20,6 @@ use std::error::Error;
use std::fmt::{Display, Formatter};
use std::rc::Rc;
use thiserror::Error;
use crate::ifs::zxdg_output_manager_v1::ZxdgOutputManagerV1Global;
#[derive(Debug, Error)]
pub enum GlobalsError {

View file

@ -20,6 +20,6 @@ pub mod zwlr_layer_shell_v1;
pub mod zwp_linux_buffer_params_v1;
pub mod zwp_linux_dmabuf_v1;
pub mod zxdg_decoration_manager_v1;
pub mod zxdg_toplevel_decoration_v1;
pub mod zxdg_output_manager_v1;
pub mod zxdg_output_v1;
pub mod zxdg_toplevel_decoration_v1;

View file

@ -10,6 +10,7 @@ use crate::wire::wl_compositor::*;
use crate::wire::WlCompositorId;
use std::rc::Rc;
use thiserror::Error;
use crate::xwayland::XWaylandEvent;
pub struct WlCompositorGlobal {
name: GlobalName,
@ -51,6 +52,9 @@ impl WlCompositor {
let surface = Rc::new(WlSurface::new(surface.id, &self.client));
track!(self.client, surface);
self.client.add_client_obj(&surface)?;
if let Some(queue) = &self.client.xwayland_queue {
queue.push(XWaylandEvent::SurfaceCreated(surface.clone()));
}
Ok(())
}

View file

@ -10,6 +10,10 @@ use bstr::ByteSlice;
use std::ffi::CString;
use std::rc::Rc;
use thiserror::Error;
use crate::drm::dma::{DmaBuf, DmaBufPlane};
use crate::drm::INVALID_MODIFIER;
use crate::ifs::wl_buffer::WlBuffer;
use crate::RenderError;
const PRIME: u32 = 1;
@ -52,7 +56,7 @@ impl Global for WlDrmGlobal {
}
fn version(&self) -> u32 {
1
2
}
}
@ -102,6 +106,60 @@ impl WlDrm {
let _req: CreatePlanarBuffer = self.client.parse(&**self, parser)?;
Err(CreatePlanarBufferError::Unsupported)
}
fn create_prime_buffer(
self: &Rc<Self>,
parser: MsgParser<'_, '_>,
) -> Result<(), CreatePrimeBufferError> {
let req: CreatePrimeBuffer = self.client.parse(&**self, parser)?;
let ctx = match self.client.state.render_ctx.get() {
Some(ctx) => ctx,
None => return Err(CreatePrimeBufferError::NoRenderContext),
};
let formats = ctx.formats();
let format = match formats.get(&req.format) {
Some(f) => *f,
None => return Err(CreatePrimeBufferError::InvalidFormat(req.format)),
};
let mut dmabuf = DmaBuf {
width: req.width,
height: req.height,
format,
modifier: INVALID_MODIFIER,
planes: vec![],
};
if req.stride0 > 0 {
dmabuf.planes.push(DmaBufPlane {
offset: req.offset0 as _,
stride: req.stride0 as _,
fd: req.name.clone(),
});
if req.stride1 > 0 {
dmabuf.planes.push(DmaBufPlane {
offset: req.offset1 as _,
stride: req.stride1 as _,
fd: req.name.clone(),
});
if req.stride2 > 0 {
dmabuf.planes.push(DmaBufPlane {
offset: req.offset2 as _,
stride: req.stride2 as _,
fd: req.name.clone(),
});
}
}
}
let img = ctx.dmabuf_img(&dmabuf)?;
let buffer = Rc::new(WlBuffer::new_dmabuf(
req.id,
&self.client,
format,
&img,
));
track!(self.client, buffer);
self.client.add_client_obj(&buffer)?;
Ok(())
}
}
object_base! {
@ -110,11 +168,12 @@ object_base! {
AUTHENTICATE => authenticate,
CREATE_BUFFER => create_buffer,
CREATE_PLANAR_BUFFER => create_planar_buffer,
CREATE_PRIME_BUFFER => create_prime_buffer,
}
impl Object for WlDrm {
fn num_requests(&self) -> u32 {
CREATE_PLANAR_BUFFER + 1
CREATE_PRIME_BUFFER + 1
}
}
@ -128,6 +187,8 @@ pub enum WlDrmError {
CreateBufferError(#[from] CreateBufferError),
#[error("Could not process a `create_planar_buffer` request")]
CreatePlanarBufferError(#[from] CreatePlanarBufferError),
#[error("Could not process a `create_prime_buffer` request")]
CreatePrimeBufferError(#[from] CreatePrimeBufferError),
#[error(transparent)]
ClientError(Box<ClientError>),
}
@ -157,3 +218,19 @@ pub enum CreatePlanarBufferError {
Unsupported,
}
efrom!(CreatePlanarBufferError, ParseError, MsgParserError);
#[derive(Debug, Error)]
pub enum CreatePrimeBufferError {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error("The compositor has no render context attached")]
NoRenderContext,
#[error("The format {0} is not supported")]
InvalidFormat(u32),
#[error("Could not import the buffer")]
ImportError(#[from] RenderError),
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(CreatePrimeBufferError, MsgParserError);
efrom!(CreatePrimeBufferError, ClientError);

View file

@ -1,22 +1,22 @@
use crate::backend::Output;
use crate::client::{Client, ClientError, ClientId};
use crate::globals::{Global, GlobalName};
use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
use crate::leaks::Tracker;
use crate::object::Object;
use crate::rect::Rect;
use crate::tree::OutputNode;
use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError;
use crate::utils::copyhashmap::CopyHashMap;
use crate::wire::wl_output::*;
use crate::wire::{WlOutputId, ZxdgOutputV1Id};
use crate::CloneCell;
use ahash::AHashMap;
use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry;
use std::rc::Rc;
use thiserror::Error;
use crate::CloneCell;
use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
use crate::rect::Rect;
use crate::tree::OutputNode;
use crate::utils::copyhashmap::CopyHashMap;
const SP_UNKNOWN: i32 = 0;
#[allow(dead_code)]
@ -83,7 +83,8 @@ impl WlOutputGlobal {
let changed = old_width != width || old_height != height;
if changed {
self.pos.set(Rect::new_sized(pos.x1(), pos.y1(), width, height).unwrap());
self.pos
.set(Rect::new_sized(pos.x1(), pos.y1(), width, height).unwrap());
let bindings = self.bindings.borrow_mut();
for binding in bindings.values() {
for binding in binding.values() {

View file

@ -1,9 +1,9 @@
mod event_handling;
mod kb_owner;
mod pointer_owner;
pub mod wl_keyboard;
pub mod wl_pointer;
pub mod wl_touch;
mod kb_owner;
use crate::async_engine::SpawnedFuture;
use crate::client::{Client, ClientError, ClientId};
@ -16,11 +16,12 @@ use crate::ifs::ipc::wl_data_source::WlDataSource;
use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1;
use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1;
use crate::ifs::ipc::IpcError;
use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_seat::kb_owner::KbOwnerHolder;
use crate::ifs::wl_seat::pointer_owner::PointerOwnerHolder;
use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE};
use crate::ifs::wl_seat::wl_pointer::WlPointer;
use crate::ifs::wl_seat::wl_touch::WlTouch;
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
use crate::ifs::wl_surface::WlSurface;
use crate::leaks::Tracker;
use crate::object::{Object, ObjectId};
@ -48,8 +49,7 @@ use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use thiserror::Error;
use uapi::{c, Errno, OwnedFd};
use crate::ifs::wl_output::{WlOutputGlobal};
use crate::ifs::wl_seat::kb_owner::KbOwnerHolder;
use crate::tree::toplevel::ToplevelNode;
const POINTER: u32 = 1;
const KEYBOARD: u32 = 2;
@ -95,7 +95,7 @@ pub struct WlSeatGlobal {
pos: Cell<(Fixed, Fixed)>,
pointer_stack: RefCell<Vec<Rc<dyn Node>>>,
found_tree: RefCell<Vec<FoundNode>>,
toplevel_focus_history: LinkedList<Rc<XdgToplevel>>,
toplevel_focus_history: LinkedList<Rc<dyn ToplevelNode>>,
keyboard_node: CloneCell<Rc<dyn Node>>,
pressed_keys: RefCell<AHashSet<u32>>,
bindings: RefCell<AHashMap<ClientId, AHashMap<WlSeatId, Rc<WlSeat>>>>,

View file

@ -8,8 +8,6 @@ use crate::ifs::wl_seat::wl_keyboard::WlKeyboard;
use crate::ifs::wl_seat::wl_pointer::{WlPointer, POINTER_FRAME_SINCE_VERSION};
use crate::ifs::wl_seat::{wl_keyboard, wl_pointer, Dnd, SeatId, WlSeat, WlSeatGlobal};
use crate::ifs::wl_surface::xdg_surface::xdg_popup::XdgPopup;
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
use crate::ifs::wl_surface::xdg_surface::XdgSurface;
use crate::ifs::wl_surface::WlSurface;
use crate::object::ObjectId;
use crate::tree::{FloatNode, Node};
@ -23,6 +21,7 @@ use i4config::keyboard::ModifiedKeySym;
use smallvec::SmallVec;
use std::ops::Deref;
use std::rc::Rc;
use crate::tree::toplevel::ToplevelNode;
#[derive(Default)]
pub struct NodeSeatState {
@ -86,7 +85,7 @@ impl NodeSeatState {
seat.ungrab_kb();
seat.keyboard_node.set(seat.state.root.clone());
if let Some(tl) = seat.toplevel_focus_history.last() {
seat.focus_xdg_surface(&tl.xdg);
seat.focus_node(tl.focus_surface(&seat));
}
}
}
@ -201,7 +200,7 @@ impl WlSeatGlobal {
self.pointer_stack.borrow().last().cloned()
}
pub fn last_tiled_keyboard_toplevel(&self, new: &dyn Node) -> Option<Rc<XdgToplevel>> {
pub fn last_tiled_keyboard_toplevel(&self, new: &dyn Node) -> Option<Rc<dyn ToplevelNode>> {
let is_container = new.is_container();
for tl in self.toplevel_focus_history.rev_iter() {
if !tl.parent_is_float() && (!is_container || !tl.is_contained_in(new.id())) {
@ -218,14 +217,10 @@ impl WlSeatGlobal {
self.extents_start_pos.set((ex.x1(), ex.y1()));
}
pub fn focus_toplevel(self: &Rc<Self>, n: &Rc<XdgToplevel>) {
pub fn focus_toplevel(self: &Rc<Self>, n: Rc<dyn ToplevelNode>) {
let node = self.toplevel_focus_history.add_last(n.clone());
n.toplevel_history.insert(self.id(), node);
self.focus_xdg_surface(&n.xdg);
}
fn focus_xdg_surface(self: &Rc<Self>, xdg: &Rc<XdgSurface>) {
self.focus_node(xdg.focus_surface(self));
n.set_focus_history_link(self, node);
self.focus_node(n.focus_surface(self));
}
fn ungrab_kb(self: &Rc<Self>) {
@ -415,7 +410,7 @@ impl WlSeatGlobal {
// Enter callbacks
impl WlSeatGlobal {
pub fn enter_toplevel(self: &Rc<Self>, n: &Rc<XdgToplevel>) {
pub fn enter_toplevel(self: &Rc<Self>, n: Rc<dyn ToplevelNode>) {
self.focus_toplevel(n);
}

View file

@ -1,7 +1,7 @@
use std::rc::Rc;
use crate::CloneCell;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::tree::Node;
use crate::CloneCell;
use std::rc::Rc;
pub struct KbOwnerHolder {
default: Rc<DefaultKbOwner>,

View file

@ -2,6 +2,7 @@ pub mod cursor;
pub mod wl_subsurface;
pub mod xdg_surface;
pub mod zwlr_layer_surface_v1;
pub mod xwindow;
use crate::backend::{KeyState, ScrollAxis};
use crate::client::{Client, ClientError, RequestParser};
@ -12,6 +13,7 @@ use crate::ifs::wl_seat::{Dnd, NodeSeatState, SeatId, WlSeatGlobal};
use crate::ifs::wl_surface::cursor::CursorSurface;
use crate::ifs::wl_surface::wl_subsurface::WlSubsurface;
use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceRole};
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error;
use crate::leaks::Tracker;
use crate::object::Object;
use crate::pixman::Region;
@ -35,7 +37,6 @@ use std::mem;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use thiserror::Error;
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error;
#[allow(dead_code)]
const INVALID_SCALE: u32 = 0;
@ -52,6 +53,7 @@ pub enum SurfaceRole {
Cursor,
DndIcon,
ZwlrLayerSurface,
XSurface,
}
impl SurfaceRole {
@ -63,6 +65,7 @@ impl SurfaceRole {
SurfaceRole::Cursor => "cursor",
SurfaceRole::DndIcon => "dnd_icon",
SurfaceRole::ZwlrLayerSurface => "zwlr_layer_surface",
SurfaceRole::XSurface => "xwayland surface",
}
}
}
@ -123,6 +126,14 @@ trait SurfaceExt {
true
}
fn on_surface_destroy(&self) -> Result<(), WlSurfaceError> {
if self.is_some() {
Err(WlSurfaceError::ReloObjectStillExists)
} else {
Ok(())
}
}
fn update_subsurface_parent_extents(&self) {
// nothing
}
@ -343,10 +354,8 @@ impl WlSurface {
let _req: Destroy = self.parse(parser)?;
self.unset_dnd_icons();
self.unset_cursors();
self.ext.get().on_surface_destroy()?;
self.destroy_node(true);
if self.ext.get().is_some() {
return Err(DestroyError::ReloObjectStillExists);
}
{
let mut children = self.children.borrow_mut();
if let Some(children) = &mut *children {
@ -784,6 +793,8 @@ pub enum WlSurfaceError {
old: SurfaceRole,
new: SurfaceRole,
},
#[error("Cannot destroy a `wl_surface` before its role object")]
ReloObjectStillExists,
}
efrom!(WlSurfaceError, ClientError);
efrom!(WlSurfaceError, XdgSurfaceError);
@ -805,11 +816,12 @@ pub enum DestroyError {
ParseFailed(#[source] Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("Cannot destroy a `wl_surface` before its role object")]
ReloObjectStillExists,
#[error(transparent)]
WlSurfaceError(Box<WlSurfaceError>),
}
efrom!(DestroyError, ParseFailed, MsgParserError);
efrom!(DestroyError, ClientError);
efrom!(DestroyError, WlSurfaceError);
#[derive(Debug, Error)]
pub enum AttachError {

View file

@ -231,12 +231,6 @@ impl XdgSurface {
}
}
pub fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc<WlSurface> {
self.focus_surface
.get(&seat.id())
.unwrap_or_else(|| self.surface.clone())
}
fn destroy_node(&self) {
self.workspace.set(None);
self.surface.destroy_node(false);

View file

@ -27,8 +27,10 @@ use std::fmt::{Debug, Formatter};
use std::mem;
use std::ops::Deref;
use std::rc::Rc;
use backtrace::Backtrace;
use thiserror::Error;
use crate::ifs::wl_surface::WlSurface;
use crate::tree::toplevel::ToplevelNode;
#[derive(Copy, Clone, Debug, FromPrimitive)]
pub enum ResizeEdge {
@ -75,7 +77,7 @@ pub struct XdgToplevel {
pub parent: CloneCell<Option<Rc<XdgToplevel>>>,
pub children: RefCell<AHashMap<XdgToplevelId, Rc<XdgToplevel>>>,
states: RefCell<AHashSet<u32>>,
pub toplevel_history: SmallMap<SeatId, LinkedNode<Rc<XdgToplevel>>, 1>,
pub toplevel_history: SmallMap<SeatId, LinkedNode<Rc<dyn ToplevelNode>>, 1>,
active_surfaces: NumCell<u32>,
pub decoration: Cell<Decoration>,
bugs: Cell<&'static Bugs>,
@ -139,13 +141,6 @@ impl XdgToplevel {
}
}
pub fn parent_is_float(&self) -> bool {
if let Some(parent) = self.parent_node.get() {
return parent.is_float();
}
false
}
fn send_configure_checked(&self, mut width: i32, mut height: i32) {
width = width.max(1);
height = height.max(1);
@ -429,7 +424,7 @@ impl Node for XdgToplevel {
}
fn do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
seat.focus_toplevel(&self);
seat.focus_toplevel(self);
}
fn absolute_position(&self) -> Rect {
@ -441,7 +436,7 @@ impl Node for XdgToplevel {
}
fn pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
seat.enter_toplevel(&self);
seat.enter_toplevel(self);
}
fn pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
@ -478,6 +473,27 @@ impl Node for XdgToplevel {
}
}
impl ToplevelNode for XdgToplevel {
fn parent(&self) -> Option<Rc<dyn Node>> {
self.parent_node.get()
}
fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc<WlSurface> {
self.xdg
.focus_surface
.get(&seat.id())
.unwrap_or_else(|| self.xdg.surface.clone())
}
fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode<Rc<dyn ToplevelNode>>) {
self.toplevel_history.insert(seat.id(), link);
}
fn as_node(&self) -> &dyn Node {
self
}
}
impl XdgSurfaceExt for XdgToplevel {
fn focus_parent(&self, seat: &Rc<WlSeatGlobal>) {
self.parent_node.get().map(|p| p.focus_self(seat));
@ -564,7 +580,7 @@ impl XdgSurfaceExt for XdgToplevel {
{
let seats = surface.client.state.globals.lock_seats();
for seat in seats.values() {
seat.focus_toplevel(&self);
seat.focus_toplevel(self.clone());
}
}
surface.client.state.tree_changed();

View file

@ -0,0 +1,247 @@
use std::cell::Cell;
use std::rc::Rc;
use thiserror::Error;
use x11rb::protocol::xproto::Window;
use i4config::Direction;
use crate::client::Client;
use crate::{AsyncQueue, CloneCell, State};
use crate::cursor::KnownCursor;
use crate::fixed::Fixed;
use crate::ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal};
use crate::ifs::wl_surface::{SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError};
use crate::rect::Rect;
use crate::render::Renderer;
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode};
use crate::tree::toplevel::ToplevelNode;
use crate::tree::walker::NodeVisitor;
use crate::utils::linkedlist::LinkedNode;
use crate::utils::smallmap::SmallMap;
use crate::wire::WlSurfaceId;
use crate::xwayland::XWaylandEvent;
pub struct XwindowData {
pub state: Rc<State>,
pub window_id: Window,
pub client: Rc<Client>,
pub surface_id: Cell<Option<WlSurfaceId>>,
pub window: CloneCell<Option<Rc<Xwindow>>>,
}
tree_id!(XwindowId);
pub struct Xwindow {
pub id: XwindowId,
pub seat_state: NodeSeatState,
pub data: Rc<XwindowData>,
pub surface: Rc<WlSurface>,
pub parent: CloneCell<Option<Rc<dyn Node>>>,
pub focus_history: SmallMap<SeatId, LinkedNode<Rc<dyn ToplevelNode>>, 1>,
pub events: Rc<AsyncQueue<XWaylandEvent>>,
pub extents: Cell<Rect>,
pub workspace: CloneCell<Option<Rc<WorkspaceNode>>>,
}
impl XwindowData {
pub fn new(state: &Rc<State>, window_id: Window, client: &Rc<Client>) -> Self {
Self {
state: state.clone(),
window_id,
client: client.clone(),
surface_id: Cell::new(None),
window: Default::default(),
}
}
}
impl Xwindow {
pub fn new(data: &Rc<XwindowData>, surface: &Rc<WlSurface>, events: &Rc<AsyncQueue<XWaylandEvent>>) -> Self {
Self {
id: data.state.node_ids.next(),
seat_state: Default::default(),
data: data.clone(),
surface: surface.clone(),
parent: Default::default(),
focus_history: Default::default(),
events: events.clone(),
extents: Default::default(),
workspace: Default::default(),
}
}
pub fn destroy(&self) {
self.break_loops();
self.data.window.take();
}
pub fn break_loops(&self) {
self.destroy_node(true);
}
pub fn install(self: &Rc<Self>) -> Result<(), XWindowError> {
self.surface.set_role(SurfaceRole::XSurface)?;
if self.surface.ext.get().is_some() {
return Err(XWindowError::AlreadyAttached);
}
self.surface.ext.set(self.clone());
Ok(())
}
fn notify_parent(&self) {
let parent = match self.parent.get() {
Some(p) => p,
_ => return,
};
let extents = self.surface.extents.get();
// let extents = self.xdg.extents.get();
// parent.child_active_changed(self, self.active_surfaces.get() > 0);
parent.child_size_changed(self, extents.width(), extents.height());
// parent.child_title_changed(self, self.title.borrow_mut().deref());
}
}
impl SurfaceExt for Xwindow {
fn post_commit(self: Rc<Self>) {
let parent = self.parent.get();
if self.surface.buffer.get().is_some() {
if parent.is_none() {
self.data.state.map_tiled(self.clone());
}
} else {
if parent.is_some() {
self.destroy_node(true);
}
}
}
fn on_surface_destroy(&self) -> Result<(), WlSurfaceError> {
self.destroy_node(true);
self.surface.unset_ext();
self.data.window.set(None);
self.data.surface_id.set(None);
self.events.push(XWaylandEvent::SurfaceDestroyed(self.surface.id));
Ok(())
}
fn extents_changed(&self) {
self.notify_parent();
}
}
impl Node for Xwindow {
fn id(&self) -> NodeId {
self.id.into()
}
fn seat_state(&self) -> &NodeSeatState {
&self.seat_state
}
fn destroy_node(&self, _detach: bool) {
self.workspace.take();
self.focus_history.clear();
if let Some(parent) = self.parent.take() {
parent.remove_child(self);
}
self.surface.destroy_node(false);
self.seat_state.destroy_node(self);
}
fn visit(self: Rc<Self>, visitor: &mut dyn NodeVisitor) {
visitor.visit_xwindow(&self);
}
fn visit_children(&self, visitor: &mut dyn NodeVisitor) {
visitor.visit_surface(&self.surface);
}
fn is_contained_in(&self, other: NodeId) -> bool {
if let Some(parent) = self.parent.get() {
if parent.id() == other {
return true;
}
return parent.is_contained_in(other);
}
false
}
fn do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
seat.focus_toplevel(self);
}
fn absolute_position(&self) -> Rect {
self.extents.get()
}
fn find_tree_at(&self, x: i32, y: i32, tree: &mut Vec<FoundNode>) -> FindTreeResult {
if let Some(buffer) = self.surface.buffer.get() {
if x < buffer.rect.width() && y < buffer.rect.height() {
tree.push(FoundNode {
node: self.surface.clone(),
x,
y,
});
return FindTreeResult::AcceptsInput;
}
}
FindTreeResult::Other
}
fn pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
seat.enter_toplevel(self);
}
fn pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_surface(&self.surface, x, y)
}
fn change_extents(self: Rc<Self>, rect: &Rect) {
let nw = rect.width();
let nh = rect.height();
let de = self.extents.replace(*rect);
if de.width() != nw || de.height() != nh {
self.events.push(XWaylandEvent::Configure(self.clone()));
}
}
fn set_workspace(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
self.workspace.set(Some(ws.clone()));
}
fn set_parent(self: Rc<Self>, parent: Rc<dyn Node>) {
self.parent.set(Some(parent));
self.notify_parent();
}
fn client(&self) -> Option<Rc<Client>> {
Some(self.data.client.clone())
}
}
impl ToplevelNode for Xwindow {
fn parent(&self) -> Option<Rc<dyn Node>> {
self.parent.get()
}
fn focus_surface(&self, _seat: &WlSeatGlobal) -> Rc<WlSurface> {
self.surface.clone()
}
fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode<Rc<dyn ToplevelNode>>) {
self.focus_history.insert(seat.id(), link);
}
fn as_node(&self) -> &dyn Node {
self
}
}
#[derive(Debug, Error)]
pub enum XWindowError {
#[error("The surface is already attached")]
AlreadyAttached,
#[error(transparent)]
WlSurfaceError(#[from] WlSurfaceError),
}

View file

@ -1,29 +1,26 @@
use std::cell::Cell;
use std::ops::Deref;
use crate::client::{Client, ClientError, ClientId};
use crate::ifs::wl_output::{WlOutput, WlOutputGlobal};
use crate::ifs::wl_surface::{CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError};
use crate::ifs::zwlr_layer_shell_v1::{OVERLAY, ZwlrLayerShellV1};
use crate::client::{Client, ClientError};
use crate::ifs::wl_seat::NodeSeatState;
use crate::ifs::wl_surface::{
CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
};
use crate::ifs::zwlr_layer_shell_v1::{ZwlrLayerShellV1, OVERLAY};
use crate::leaks::Tracker;
use crate::object::Object;
use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError;
use crate::wire::zwlr_layer_surface_v1::*;
use crate::wire::{WlSurfaceId, ZwlrLayerSurfaceV1Id};
use std::rc::Rc;
use thiserror::Error;
use i4config::Direction;
use crate::ifs::wl_surface::wl_subsurface::WlSubsurface;
use crate::{CloneCell, NumCell};
use crate::backend::{KeyState, ScrollAxis};
use crate::fixed::Fixed;
use crate::ifs::wl_seat::{Dnd, NodeSeatState, WlSeatGlobal};
use crate::rect::Rect;
use crate::render::Renderer;
use crate::tree::{ContainerNode, ContainerSplit, FindTreeResult, FloatNode, FoundNode, Node, NodeId, OutputNode, WorkspaceNode};
use crate::tree::walker::NodeVisitor;
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, OutputNode};
use crate::utils::bitflags::BitflagsExt;
use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError;
use crate::utils::linkedlist::LinkedNode;
use crate::wire::zwlr_layer_surface_v1::*;
use crate::wire::{WlSurfaceId, ZwlrLayerSurfaceV1Id};
use crate::NumCell;
use std::cell::Cell;
use std::ops::Deref;
use std::rc::Rc;
use thiserror::Error;
const KI_NONE: u32 = 0;
#[allow(dead_code)]
@ -100,7 +97,7 @@ impl ZwlrLayerSurfaceV1 {
margin: Cell::new((0, 0, 0, 0)),
keyboard_interactivity: Cell::new(0),
link: Cell::new(None),
seat_state: Default::default()
seat_state: Default::default(),
}
}
@ -132,7 +129,9 @@ impl ZwlrLayerSurfaceV1 {
if req.width > u16::MAX as u32 || req.height > u16::MAX as u32 {
return Err(SetSizeError::ExcessiveSize);
}
self.pending.size.set(Some((req.width as _, req.height as _)));
self.pending
.size
.set(Some((req.width as _, req.height as _)));
Ok(())
}
@ -153,16 +152,25 @@ impl ZwlrLayerSurfaceV1 {
fn set_margin(&self, parser: MsgParser<'_, '_>) -> Result<(), SetMarginError> {
let req: SetMargin = self.client.parse(self, parser)?;
self.pending.margin.set(Some((req.top, req.right, req.bottom, req.left)));
self.pending
.margin
.set(Some((req.top, req.right, req.bottom, req.left)));
Ok(())
}
fn set_keyboard_interactivity(&self, parser: MsgParser<'_, '_>) -> Result<(), SetKeyboardInteractivityError> {
fn set_keyboard_interactivity(
&self,
parser: MsgParser<'_, '_>,
) -> Result<(), SetKeyboardInteractivityError> {
let req: SetKeyboardInteractivity = self.client.parse(self, parser)?;
if req.keyboard_interactivity > KI_ON_DEMAND {
return Err(SetKeyboardInteractivityError::UnknownKi(req.keyboard_interactivity));
return Err(SetKeyboardInteractivityError::UnknownKi(
req.keyboard_interactivity,
));
}
self.pending.keyboard_interactivity.set(Some(req.keyboard_interactivity));
self.pending
.keyboard_interactivity
.set(Some(req.keyboard_interactivity));
Ok(())
}
@ -271,7 +279,8 @@ impl ZwlrLayerSurfaceV1 {
} else if anchor.contains(BOTTOM) {
y1 += opos.height() - height;
}
self.pos.set(Rect::new_sized(x1, y1, width, height).unwrap());
self.pos
.set(Rect::new_sized(x1, y1, width, height).unwrap());
self.client.state.tree_changed();
}
}
@ -308,7 +317,7 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
if was_active {
self.surface.active_changed(false);
}
},
}
KI_ON_DEMAND => self.surface.seat_state.release_kb_grab(),
KI_EXCLUSIVE => {
let seats = self.client.state.globals.seats.lock();
@ -362,7 +371,7 @@ impl Node for ZwlrLayerSurfaceV1 {
tree.push(FoundNode {
node: self.surface.clone(),
x,
y
y,
});
self.surface.find_tree_at(x, y, tree)
}

View file

@ -1,14 +1,14 @@
use crate::client::{Client, ClientError};
use crate::globals::{Global, GlobalName};
use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
use crate::leaks::Tracker;
use crate::object::Object;
use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError;
use crate::wire::zxdg_output_manager_v1::*;
use crate::wire::{ZxdgOutputManagerV1Id};
use crate::wire::ZxdgOutputManagerV1Id;
use std::rc::Rc;
use thiserror::Error;
use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
pub struct ZxdgOutputManagerV1Global {
name: GlobalName,
@ -51,10 +51,7 @@ impl ZxdgOutputManagerV1 {
Ok(())
}
fn get_xdg_output(
self: &Rc<Self>,
parser: MsgParser<'_, '_>,
) -> Result<(), GetXdgOutputError> {
fn get_xdg_output(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), GetXdgOutputError> {
let req: GetXdgOutput = self.client.parse(&**self, parser)?;
let output = self.client.lookup(req.output)?;
let xdg_output = Rc::new(ZxdgOutputV1 {
@ -62,7 +59,7 @@ impl ZxdgOutputManagerV1 {
version: self.version,
client: self.client.clone(),
output: output.clone(),
tracker: Default::default()
tracker: Default::default(),
});
track!(self.client, xdg_output);
self.client.add_client_obj(&xdg_output)?;

View file

@ -1,14 +1,16 @@
use std::rc::Rc;
use thiserror::Error;
use crate::client::{Client, ClientError};
use crate::ifs::wl_output::{SEND_DONE_SINCE, WlOutput};
use crate::ifs::wl_output::{WlOutput, SEND_DONE_SINCE};
use crate::leaks::Tracker;
use crate::object::Object;
use crate::utils::buffd::{MsgParser, MsgParserError};
use crate::wire::ZxdgOutputV1Id;
use crate::wire::zxdg_output_v1::*;
use crate::wire::ZxdgOutputV1Id;
use std::rc::Rc;
use thiserror::Error;
#[allow(dead_code)]
pub const NAME_SINCE: u32 = 2;
#[allow(dead_code)]
pub const DESCRIPTION_SINCE: u32 = 2;
pub const NO_DONE_SINCE: u32 = 3;
@ -38,11 +40,10 @@ impl ZxdgOutputV1 {
}
pub fn send_done(&self) {
self.client.event(Done {
self_id: self.id,
});
self.client.event(Done { self_id: self.id });
}
#[allow(dead_code)]
pub fn send_name(&self, name: &str) {
self.client.event(Name {
self_id: self.id,
@ -50,6 +51,7 @@ impl ZxdgOutputV1 {
});
}
#[allow(dead_code)]
pub fn send_description(&self, description: &str) {
self.client.event(Description {
self_id: self.id,

View file

@ -1,4 +1,4 @@
#![feature(c_variadic, thread_local, label_break_value)]
#![feature(c_variadic, thread_local, label_break_value, try_blocks)]
#![allow(
clippy::len_zero,
clippy::needless_lifetimes,
@ -178,7 +178,9 @@ fn main_() -> Result<(), MainError> {
let _float_render_titles = engine.spawn2(Phase::PostLayout, float_titles(state.clone()));
let socket_path = Acceptor::install(&state)?;
forker.setenv(b"WAYLAND_DISPLAY", socket_path.as_bytes());
let _xwayland = engine.spawn(xwayland::manage(state.clone()));
el.run()?;
drop(_xwayland);
state.clients.clear();
for (_, seat) in state.globals.seats.lock().deref() {
seat.clear();

View file

@ -1,6 +1,7 @@
use crate::format::{Format, ARGB8888};
use crate::ifs::wl_buffer::WlBuffer;
use crate::ifs::wl_surface::xdg_surface::XdgSurface;
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use crate::ifs::wl_surface::WlSurface;
use crate::rect::Rect;
use crate::render::gl::frame_buffer::{with_scissor, GlFrameBuffer};
@ -21,7 +22,6 @@ use crate::State;
use std::ops::Deref;
use std::rc::Rc;
use std::slice;
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
const NON_COLOR: Color = Color::from_rgbaf(0.2, 0.2, 0.2, 1.0);
const CHILD_COLOR: Color = Color::from_rgbaf(0.8, 0.8, 0.8, 1.0);
@ -49,7 +49,7 @@ impl Renderer<'_> {
let pos = ls.position();
self.render_layer_surface(ls.deref(), pos.x1(), pos.y1());
}
}
};
}
render_layer!(output.layers[0]);
render_layer!(output.layers[1]);

View file

@ -117,9 +117,9 @@ impl State {
let seat = self.seat_queue.last();
if let Some(seat) = seat {
if let Some(prev) = seat.last_tiled_keyboard_toplevel(&*node) {
if let Some(container) = prev.parent_node.get() {
if let Some(container) = prev.parent() {
if let Some(container) = container.into_container() {
container.add_child_after(&*prev, node);
container.add_child_after(prev.as_node(), node);
return;
}
}

View file

@ -14,17 +14,18 @@ use crate::NumCell;
pub use container::*;
pub use float::*;
use i4config::Direction;
pub use output::*;
use std::fmt::{Debug, Display};
use std::ops::Deref;
use std::rc::Rc;
pub use workspace::*;
pub use output::*;
mod container;
mod float;
mod output;
pub mod walker;
mod workspace;
mod output;
pub mod toplevel;
pub struct NodeIds {
next: NumCell<u32>,

View file

@ -1,17 +1,17 @@
use std::cell::{Cell, RefCell};
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use std::rc::Rc;
use crate::{CloneCell, DisplayNode};
use crate::cursor::KnownCursor;
use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal};
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use crate::rect::Rect;
use crate::render::Renderer;
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode};
use crate::tree::walker::NodeVisitor;
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode};
use crate::utils::linkedlist::LinkedList;
use crate::{CloneCell, DisplayNode};
use std::cell::{Cell, RefCell};
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use std::rc::Rc;
tree_id!(OutputNodeId);
pub struct OutputNode {

19
src/tree/toplevel.rs Normal file
View file

@ -0,0 +1,19 @@
use std::rc::Rc;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::wl_surface::WlSurface;
use crate::tree::Node;
use crate::utils::linkedlist::LinkedNode;
pub trait ToplevelNode: Node {
fn parent(&self) -> Option<Rc<dyn Node>>;
fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc<WlSurface>;
fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode<Rc<dyn ToplevelNode>>);
fn as_node(&self) -> &dyn Node;
fn parent_is_float(&self) -> bool {
if let Some(parent) = self.parent() {
return parent.is_float();
}
false
}
}

View file

@ -1,10 +1,11 @@
use crate::ifs::wl_surface::xdg_surface::xdg_popup::XdgPopup;
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use crate::ifs::wl_surface::WlSurface;
use crate::tree::{ContainerNode, FloatNode, Node, OutputNode, WorkspaceNode};
use crate::DisplayNode;
use std::rc::Rc;
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use crate::ifs::wl_surface::xwindow::Xwindow;
pub trait NodeVisitorBase: Sized {
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
@ -42,6 +43,10 @@ pub trait NodeVisitorBase: Sized {
fn visit_layer_surface(&mut self, node: &Rc<ZwlrLayerSurfaceV1>) {
node.visit_children(self);
}
fn visit_xwindow(&mut self, node: &Rc<Xwindow>) {
node.visit_children(self);
}
}
pub trait NodeVisitor {
@ -54,6 +59,7 @@ pub trait NodeVisitor {
fn visit_float(&mut self, node: &Rc<FloatNode>);
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>);
fn visit_layer_surface(&mut self, node: &Rc<ZwlrLayerSurfaceV1>);
fn visit_xwindow(&mut self, node: &Rc<Xwindow>);
}
impl<T: NodeVisitorBase> NodeVisitor for T {
@ -92,6 +98,10 @@ impl<T: NodeVisitorBase> NodeVisitor for T {
fn visit_layer_surface(&mut self, node: &Rc<ZwlrLayerSurfaceV1>) {
<T as NodeVisitorBase>::visit_layer_surface(self, node)
}
fn visit_xwindow(&mut self, node: &Rc<Xwindow>) {
<T as NodeVisitorBase>::visit_xwindow(self, node)
}
}
// pub fn visit_containers<F: FnMut(&Rc<ContainerNode>)>(f: F) -> impl NodeVisitor {

View file

@ -1,11 +1,16 @@
pub trait BitflagsExt {
fn contains(self, other: Self) -> bool;
fn intersects(self, other: Self) -> bool;
}
macro_rules! num {
($ty:ident) => {
impl BitflagsExt for $ty {
fn contains(self, other: Self) -> bool {
self & other == other
}
fn intersects(self, other: Self) -> bool {
self & other != 0
}
}

View file

@ -74,7 +74,11 @@ impl BufFdIn {
name: uapi::sockaddr_none_mut(),
flags: 0,
};
let (iov, _, mut cmsg) = match uapi::recvmsg(self.fd.raw(), &mut hdr, c::MSG_DONTWAIT) {
let (iov, _, mut cmsg) = match uapi::recvmsg(
self.fd.raw(),
&mut hdr,
c::MSG_DONTWAIT | c::MSG_CMSG_CLOEXEC,
) {
Ok((iov, _, _)) if iov.is_empty() => return Err(BufFdError::Closed),
Ok(v) => v,
Err(Errno(c::EAGAIN)) => return Ok(true),

View file

@ -157,7 +157,11 @@ impl BufFdOut {
Ok(false)
}
pub async fn flush2(&mut self, buf: &[u8], fds: &mut Vec<Rc<OwnedFd>>) -> Result<(), BufFdError> {
pub async fn flush2(
&mut self,
buf: &[u8],
fds: &mut Vec<Rc<OwnedFd>>,
) -> Result<(), BufFdError> {
let mut read_pos = 0;
while read_pos < buf.len() {
if self.flush_sync2(&mut read_pos, buf, fds)? {

View file

@ -12,4 +12,5 @@ pub mod ptr_ext;
pub mod queue;
pub mod smallmap;
pub mod stack;
pub mod tri;
pub mod vec_ext;

View file

@ -39,6 +39,10 @@ impl<T> AsyncQueue<T> {
AsyncQueuePop { queue: self }
}
pub fn non_empty<'a>(&'a self) -> AsyncQueueNonEmpty<'a, T> {
AsyncQueueNonEmpty { queue: self }
}
pub fn clear(&self) {
mem::take(&mut *self.data.borrow_mut());
self.waiter.take();
@ -61,3 +65,20 @@ impl<'a, T> Future for AsyncQueuePop<'a, T> {
}
}
}
pub struct AsyncQueueNonEmpty<'a, T> {
queue: &'a AsyncQueue<T>,
}
impl<'a, T> Future for AsyncQueueNonEmpty<'a, T> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.queue.data.borrow_mut().len() > 0 {
Poll::Ready(())
} else {
self.queue.waiter.set(Some(cx.waker().clone()));
Poll::Pending
}
}
}

49
src/utils/tri.rs Normal file
View file

@ -0,0 +1,49 @@
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
pub trait Try: Sized {
fn tri<F>(f: F) -> Result<(), Self>
where
F: FnOnce() -> Result<(), Self>;
fn tria<F>(f: F) -> Tria<Self, F>
where
F: Future<Output = Result<(), Self>>;
}
impl<E> Try for E {
fn tri<F>(f: F) -> Result<(), Self>
where
F: FnOnce() -> Result<(), Self>,
{
f()
}
fn tria<F>(f: F) -> Tria<E, F>
where
F: Future<Output = Result<(), Self>>,
{
Tria {
f,
_phantom: Default::default(),
}
}
}
pub struct Tria<E, F> {
f: F,
_phantom: PhantomData<E>,
}
impl<E, F> Future for Tria<E, F>
where
F: Future<Output = Result<(), E>>,
{
type Output = Result<(), E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe { Pin::new_unchecked(&mut Pin::get_unchecked_mut(self).f).poll(cx) }
}
}

View file

@ -0,0 +1,245 @@
mod xsocket;
mod xwm;
use crate::forker::ForkerProxy;
use crate::utils::tri::Try;
use crate::xwayland::xsocket::allocate_socket;
use crate::xwayland::xwm::Wm;
use crate::{AsyncError, AsyncQueue, ErrorFmt, ForkerError, State};
use bstr::ByteSlice;
use std::error::Error;
use std::num::ParseIntError;
use std::rc::Rc;
use thiserror::Error;
use uapi::{c, Errno, OwnedFd, pipe2};
use crate::client::ClientError;
use crate::ifs::wl_surface::WlSurface;
use crate::ifs::wl_surface::xwindow::Xwindow;
use crate::wire::WlSurfaceId;
#[derive(Debug, Error)]
enum XWaylandError {
#[error("Could not create a wayland socket")]
SocketFailed(#[source] std::io::Error),
#[error("/tmp/.X11-unix does not exist")]
MissingSocketDir,
#[error("Could not stat /tmp/.X11-unix")]
StatSocketDir(#[source] std::io::Error),
#[error("/tmp/.X11-unix is not a directory")]
NotASocketDir,
#[error("/tmp/.X11-unix is writable")]
SocketDirNotWritable,
#[error("Could not write to the lock file")]
WriteLockFile(#[source] std::io::Error),
#[error("Could not open the lock file for reading")]
ReadLockFile(#[source] std::io::Error),
#[error("The lock file does not contain a PID")]
NotALockFile(#[source] ParseIntError),
#[error("The socket is already in use")]
AlreadyInUse,
#[error("Could not bind the socket to an address")]
BindFailed(#[source] std::io::Error),
#[error("All X displays in the range 0..1000 are already in use")]
AddressesInUse,
#[error("The async engine returned an error")]
AsyncError(#[from] AsyncError),
#[error("pipe(2) failed")]
Pipe(#[source] std::io::Error),
#[error("dupfd(2) failed")]
Dupfd(#[source] std::io::Error),
#[error("socketpair(2) failed")]
Socketpair(#[source] std::io::Error),
#[error("Could not start Xwayland")]
ExecFailed(#[source] ForkerError),
#[error("Could not load the atoms")]
LoadAtoms(#[source] Box<dyn Error>),
#[error("Could not connect to Xwayland")]
Connect(#[source] Box<dyn Error>),
#[error("Could not create a window manager")]
CreateWm(#[source] Box<Self>),
#[error("Could not select the root events")]
SelectRootEvents(#[source] Box<dyn Error>),
#[error("Could not create the WM window")]
CreateXWindow(#[source] Box<dyn Error>),
#[error("Could not acquire a selection")]
SelectionOwner(#[source] Box<dyn Error>),
#[error("composite_redirect_subwindows failed")]
CompositeRedirectSubwindows(#[source] Box<dyn Error>),
#[error("Could not spawn the Xwayland client")]
SpawnClient(#[source] ClientError),
}
pub async fn manage(state: Rc<State>) {
loop {
let forker = match state.forker.get() {
Some(f) => f,
None => {
log::error!("There is no forker. Cannot start Xwayland.");
return;
}
};
let (xsocket, socket) = match allocate_socket() {
Ok(s) => s,
Err(e) => {
log::error!("Could not allocate a socket for Xwayland: {}", ErrorFmt(e));
return;
}
};
if let Err(e) = uapi::listen(socket.raw(), 4096) {
log::error!("Could not listen on the Xwayland socket: {}", ErrorFmt(e));
}
forker.setenv(b"DISPLAY", format!(":{}", xsocket.id).as_bytes());
log::info!("Allocated display :{} for Xwayland", xsocket.id);
log::info!("Waiting for connection attempt");
let res = XWaylandError::tria(async {
let _ = state.eng.fd(&socket)?.readable().await;
Ok(())
})
.await;
if let Err(e) = res {
log::error!("{}", ErrorFmt(e));
return;
}
log::info!("Starting Xwayland");
if let Err(e) = run(&state, &forker, socket).await {
log::error!("Xwayland failed: {}", ErrorFmt(e));
} else {
log::warn!("Xwayland exited unexpectedly");
}
forker.unsetenv(b"DISPLAY");
}
}
async fn run(
state: &Rc<State>,
forker: &Rc<ForkerProxy>,
socket: Rc<OwnedFd>,
) -> Result<(), XWaylandError> {
let (dfdread, dfdwrite) = match pipe2(c::O_CLOEXEC) {
Ok(p) => p,
Err(e) => return Err(XWaylandError::Pipe(e.into())),
};
let (stderr_read, stderr_write) = match pipe2(c::O_CLOEXEC) {
Ok(p) => p,
Err(e) => return Err(XWaylandError::Pipe(e.into())),
};
let wm = uapi::socketpair(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0);
let (wm1, wm2) = match wm {
Ok(w) => w,
Err(e) => return Err(XWaylandError::Socketpair(e.into())),
};
let client = uapi::socketpair(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC | c::SOCK_NONBLOCK, 0);
let (client1, client2) = match client {
Ok(w) => w,
Err(e) => return Err(XWaylandError::Socketpair(e.into())),
};
let stderr_read = state.eng.spawn(log_xwayland(state.clone(), stderr_read));
let pidfd = forker
.xwayland(
Rc::new(stderr_write),
Rc::new(dfdwrite),
socket,
Rc::new(wm2),
Rc::new(client2),
)
.await;
let pidfd = match pidfd {
Ok(p) => p,
Err(e) => return Err(XWaylandError::ExecFailed(e)),
};
let client_id = state.clients.id();
let queue = Rc::new(AsyncQueue::new());
let client = state.clients.spawn2(client_id, state, client1, 9999, 9999, Some(queue.clone()));
let client = match client {
Ok(c) => c,
Err(e) => return Err(XWaylandError::SpawnClient(e)),
};
let _ = state.eng.fd(&Rc::new(dfdread))?.readable().await;
let wm = match Wm::get(state, client, wm1, queue.clone()) {
Ok(w) => w,
Err(e) => return Err(XWaylandError::CreateWm(Box::new(e))),
};
let wm = state.eng.spawn(wm.run());
let _ = state.eng.fd(&Rc::new(pidfd))?.readable().await;
drop(wm);
queue.clear();
stderr_read.await;
Ok(())
}
pub fn build_args(fds: &[OwnedFd]) -> (String, Vec<String>) {
let prog = "Xwayland".to_string();
let mut args = vec![];
args.push("-terminate".to_string());
args.push("-rootless".to_string());
args.push("-verbose".to_string());
args.push(10.to_string());
args.push("-displayfd".to_string());
args.push(fds[0].raw().to_string());
args.push("-listenfd".to_string());
args.push(fds[1].raw().to_string());
args.push("-wm".to_string());
args.push(fds[2].raw().to_string());
(prog, args)
}
async fn log_xwayland(state: Rc<State>, stderr: OwnedFd) {
let res = Errno::tri(|| {
uapi::fcntl_setfl(
stderr.raw(),
uapi::fcntl_getfl(stderr.raw())? | c::O_NONBLOCK,
)?;
Ok(())
});
if let Err(e) = res {
log::error!("Could not set stderr fd to nonblock: {}", ErrorFmt(e));
return;
}
let afd = match state.eng.fd(&Rc::new(stderr)) {
Ok(f) => f,
Err(e) => {
log::error!(
"Could not turn the stderr fd into an async fd: {}",
ErrorFmt(e)
);
return;
}
};
let mut buf = vec![];
let mut buf2 = [0; 128];
let mut done = false;
while !done {
let _ = afd.readable().await;
loop {
match uapi::read(afd.raw(), &mut buf2[..]) {
Ok(buf2) if buf2.len() > 0 => {
buf.extend_from_slice(buf2);
}
Ok(_) => {
done = true;
break;
}
Err(Errno(c::EAGAIN)) => {
break;
}
Err(e) => {
log::error!(
"Could not read from stderr fd: {}",
ErrorFmt(std::io::Error::from(e))
);
return;
}
}
}
for line in buf.lines() {
log::info!("Xwayland: {}", line.as_bstr());
}
buf.clear();
}
}
pub enum XWaylandEvent {
SurfaceCreated(Rc<WlSurface>),
SurfaceDestroyed(WlSurfaceId),
Configure(Rc<Xwindow>),
}

103
src/xwayland/xsocket.rs Normal file
View file

@ -0,0 +1,103 @@
use crate::xwayland::XWaylandError;
use crate::ErrorFmt;
use std::io::{Read, Write};
use std::rc::Rc;
use uapi::{c, format_ustr, Errno, OwnedFd, Ustring};
const SOCK_DIR: &str = "/tmp/.X11-unix";
pub struct XSocket {
pub id: u32,
pub path: Ustring,
pub lock_path: Ustring,
}
impl Drop for XSocket {
fn drop(&mut self) {
let _ = uapi::unlink(&self.path);
let _ = uapi::unlink(&self.lock_path);
}
}
fn bind_socket(fd: &Rc<OwnedFd>, id: u32) -> Result<(XSocket, Rc<OwnedFd>), XWaylandError> {
let path = format_ustr!("{}/X{}", SOCK_DIR, id);
let lock_path = format_ustr!("/tmp/.X{}-lock", id);
let mut lock_fd = 'open_lock_file: {
for i in 0..2 {
if let Ok(fd) = uapi::open(
&*lock_path,
c::O_CREAT | c::O_CLOEXEC | c::O_WRONLY | c::O_EXCL,
0o444,
) {
break 'open_lock_file fd;
}
if i == 1 {
return Err(XWaylandError::AlreadyInUse);
}
let mut fd = match uapi::open(&*lock_path, c::O_CLOEXEC | c::O_RDONLY, 0) {
Ok(f) => f,
Err(e) => return Err(XWaylandError::ReadLockFile(e.into())),
};
let mut pid = String::new();
if let Err(e) = fd.read_to_string(&mut pid) {
return Err(XWaylandError::ReadLockFile(e.into()));
}
let pid = match pid.trim().parse() {
Ok(p) => p,
Err(e) => return Err(XWaylandError::NotALockFile(e)),
};
match uapi::kill(pid, 0) {
Err(Errno(c::ESRCH)) => {
let _ = uapi::unlink(&lock_path);
}
_ => return Err(XWaylandError::AlreadyInUse),
}
}
return Err(XWaylandError::AlreadyInUse);
};
let _ = uapi::unlink(&path);
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
addr.sun_family = c::AF_UNIX as _;
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
sun_path[..path.len()].copy_from_slice(path.as_bytes());
sun_path[path.len()] = 0;
if let Err(e) = uapi::bind(fd.raw(), &addr) {
return Err(XWaylandError::BindFailed(e.into()));
}
let s = format!("{:10}\n", uapi::getpid());
if let Err(e) = lock_fd.write_all(s.as_bytes()) {
return Err(XWaylandError::WriteLockFile(e.into()));
}
let xsocket = XSocket {
id,
path,
lock_path,
};
Ok((xsocket, fd.clone()))
}
pub(super) fn allocate_socket() -> Result<(XSocket, Rc<OwnedFd>), XWaylandError> {
match uapi::stat(SOCK_DIR) {
Err(Errno(c::ENOENT)) => return Err(XWaylandError::MissingSocketDir),
Err(e) => return Err(XWaylandError::StatSocketDir(e.into())),
Ok(s) if s.st_mode & c::S_IFMT != c::S_IFDIR => return Err(XWaylandError::NotASocketDir),
_ => {
if uapi::access(SOCK_DIR, c::W_OK).is_err() {
return Err(XWaylandError::SocketDirNotWritable);
}
}
}
let fd = match uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0) {
Ok(f) => Rc::new(f),
Err(e) => return Err(XWaylandError::SocketFailed(e.into())),
};
for i in 0..1000 {
match bind_socket(&fd, i) {
Ok(s) => return Ok(s),
Err(e) => {
log::warn!("Cannot use the :{} display: {}", i, ErrorFmt(e));
}
}
}
Err(XWaylandError::AddressesInUse)
}

354
src/xwayland/xwm.rs Normal file
View file

@ -0,0 +1,354 @@
use std::error::Error;
use crate::async_engine::AsyncFd;
use crate::xwayland::{XWaylandError, XWaylandEvent};
use crate::{AsyncQueue, ErrorFmt, State};
use std::os::unix::io::FromRawFd;
use std::os::unix::net::UnixStream;
use std::rc::Rc;
use ahash::AHashMap;
use futures::FutureExt;
use uapi::OwnedFd;
use x11rb::atom_manager;
use x11rb::connection::Connection;
use x11rb::errors::ConnectionError;
use x11rb::protocol::composite::{ConnectionExt as _, Redirect};
use x11rb::protocol::xproto::{ChangeWindowAttributesAux, CreateWindowAux, ConnectionExt as _, EventMask, Window, WindowClass, ClientMessageEvent, CreateNotifyEvent, DestroyNotifyEvent, ConfigureWindowAux, MapRequestEvent, ConfigureRequestEvent};
use x11rb::protocol::Event;
use x11rb::rust_connection::{DefaultStream, RustConnection};
use crate::client::Client;
use crate::ifs::wl_surface::WlSurface;
use crate::wire::WlSurfaceId;
use crate::ifs::wl_surface::xwindow::{Xwindow, XwindowData};
atom_manager! {
pub Atoms: AtomsCookie {
WL_SURFACE_ID,
WM_DELETE_WINDOW,
WM_PROTOCOLS,
WM_HINTS,
WM_NORMAL_HINTS,
WM_SIZE_HINTS,
WM_WINDOW_ROLE,
MOTIF_WM_HINTS,
UTF8_STRING,
WM_S0,
NET_SUPPORTED,
NET_WM_CM_S0,
NET_WM_PID,
NET_WM_NAME,
NET_WM_STATE,
NET_WM_WINDOW_TYPE,
WM_TAKE_FOCUS,
WINDOW,
NET_ACTIVE_WINDOW,
NET_WM_MOVERESIZE,
NET_SUPPORTING_WM_CHECK,
NET_WM_STATE_FOCUSED,
NET_WM_STATE_MODAL,
NET_WM_STATE_FULLSCREEN,
NET_WM_STATE_MAXIMIZED_VERT,
NET_WM_STATE_MAXIMIZED_HORZ,
NET_WM_STATE_HIDDEN,
NET_WM_PING,
WM_CHANGE_STATE,
WM_STATE,
CLIPBOARD,
PRIMARY,
WL_SELECTION,
TARGETS,
CLIPBOARD_MANAGER,
INCR,
TEXT,
TIMESTAMP,
DELETE,
NET_STARTUP_ID,
NET_STARTUP_INFO,
NET_STARTUP_INFO_BEGIN,
NET_WM_WINDOW_TYPE_NORMAL,
NET_WM_WINDOW_TYPE_UTILITY,
NET_WM_WINDOW_TYPE_TOOLTIP,
NET_WM_WINDOW_TYPE_DND,
NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
NET_WM_WINDOW_TYPE_POPUP_MENU,
NET_WM_WINDOW_TYPE_COMBO,
NET_WM_WINDOW_TYPE_MENU,
NET_WM_WINDOW_TYPE_NOTIFICATION,
NET_WM_WINDOW_TYPE_SPLASH,
DND_SELECTION,
DND_AWARE,
DND_STATUS,
DND_POSITION,
DND_ENTER,
DND_LEAVE,
DND_DROP,
DND_FINISHED,
DND_PROXY,
DND_TYPE_LIST,
DND_ACTION_MOVE,
DND_ACTION_COPY,
DND_ACTION_ASK,
DND_ACTION_PRIVATE,
NET_CLIENT_LIST,
NET_CLIENT_LIST_STACKING,
}
}
type Res<T> = Result<T, Box<dyn Error>>;
pub struct Wm {
state: Rc<State>,
c: RustConnection,
atoms: Atoms,
socket: AsyncFd,
root: Window,
xwin: Window,
client: Rc<Client>,
windows: AHashMap<Window, Rc<XwindowData>>,
windows_by_surface_id: AHashMap<WlSurfaceId, Rc<XwindowData>>,
queue: Rc<AsyncQueue<XWaylandEvent>>,
}
impl Drop for Wm {
fn drop(&mut self) {
for (_, window) in self.windows.drain() {
if let Some(window) = window.window.take() {
window.break_loops();
}
}
}
}
impl Wm {
pub(super) fn get(state: &Rc<State>, client: Rc<Client>, socket: OwnedFd, queue: Rc<AsyncQueue<XWaylandEvent>>) -> Result<Self, XWaylandError> {
let socket_dup = match uapi::fcntl_dupfd_cloexec(socket.raw(), 0) {
Ok(s) => state.eng.fd(&Rc::new(s))?,
Err(e) => return Err(XWaylandError::Dupfd(e.into())),
};
let c = try {
RustConnection::connect_to_stream(
DefaultStream::from_unix_stream(unsafe {
UnixStream::from_raw_fd(socket.unwrap())
})?,
0,
)?
};
let c: RustConnection = match c {
Ok(c) => c,
Err(e) => return Err(XWaylandError::Connect(e)),
};
let atoms: Atoms = match try { Atoms::new(&c)?.reply()? } {
Ok(a) => a,
Err(e) => return Err(XWaylandError::LoadAtoms(e)),
};
let root = c.setup().roots[0].root;
{
let cwa = ChangeWindowAttributesAux::new().event_mask(
EventMask::SUBSTRUCTURE_NOTIFY
| EventMask::SUBSTRUCTURE_REDIRECT
| EventMask::PROPERTY_CHANGE,
);
let res = try { c.change_window_attributes(root, &cwa)?.check()? };
if let Err(e) = res {
return Err(XWaylandError::SelectRootEvents(e));
}
}
{
let res = try { c.composite_redirect_subwindows(root, Redirect::MANUAL)?.check()? };
if let Err(e) = res {
return Err(XWaylandError::CompositeRedirectSubwindows(e));
}
}
let xwin = c.generate_id().unwrap_or(0);
{
let res = try {
c.create_window(
0,
xwin,
root,
0,
0,
10,
10,
0,
WindowClass::INPUT_OUTPUT,
0,
&CreateWindowAux::new(),
)?
.check()?;
};
if let Err(e) = res {
return Err(XWaylandError::CreateXWindow(e));
}
}
{
let res = try {
c.set_selection_owner(xwin, atoms.WM_S0, 0u32)?.check()?;
};
if let Err(e) = res {
return Err(XWaylandError::SelectionOwner(e));
}
}
Ok(Self {
state: state.clone(),
c,
atoms,
socket: socket_dup,
root,
xwin,
client,
windows: Default::default(),
windows_by_surface_id: Default::default(),
queue,
})
}
pub async fn run(mut self) {
loop {
while let Some(e) = self.queue.try_pop() {
self.handle_xwayland_event(e);
}
if let Err(e) = self.handle_events() {
log::error!("Connection failed: {}", ErrorFmt(e));
return;
}
futures::select! {
_ = self.socket.readable().fuse() => { },
_ = self.queue.non_empty().fuse() => { },
}
}
}
fn handle_xwayland_event(&mut self, e: XWaylandEvent) {
match e {
XWaylandEvent::SurfaceCreated(event) => self.handle_xwayland_surface_created(event),
XWaylandEvent::Configure(event) => self.handle_xwayland_configure(event),
XWaylandEvent::SurfaceDestroyed(event) => self.handle_xwayland_surface_destroyed(event),
}
}
fn handle_xwayland_configure(&mut self, window: Rc<Xwindow>) {
self.send_configure(window);
}
fn send_configure(&mut self, window: Rc<Xwindow>) {
let extents = window.extents.get();
let cfg = ConfigureWindowAux::new()
.x(extents.x1())
.y(extents.y1())
.width(extents.width() as u32)
.height(extents.height() as u32)
.border_width(0);
let res: Res<()> = try {
self.c.configure_window(window.data.window_id, &cfg)?.check()?;
};
if let Err(e) = res {
log::error!("Could not configure window: {}", ErrorFmt(&*e));
}
}
fn create_window(&mut self, data: &Rc<XwindowData>, surface: Rc<WlSurface>) {
if data.window.get().is_some() {
log::error!("The xwindow has already been constructed");
return;
}
let window = Rc::new(Xwindow::new(&data, &surface, &self.queue));
if let Err(e) = window.install() {
log::error!("Could not attach the xwindow to the surface: {}", ErrorFmt(e));
return;
}
data.window.set(Some(window.clone()));
if surface.buffer.get().is_some() {
self.state.map_tiled(window);
}
}
fn handle_xwayland_surface_created(&mut self, surface: Rc<WlSurface>) {
let data = match self.windows_by_surface_id.get(&surface.id) {
Some(w) => w.clone(),
_ => return,
};
self.create_window(&data, surface);
}
fn handle_xwayland_surface_destroyed(&mut self, surface: WlSurfaceId) {
self.windows_by_surface_id.remove(&surface);
}
fn handle_events(&mut self) -> Result<(), ConnectionError> {
while let Some(e) = self.c.poll_for_event()? {
self.handle_event(e);
}
Ok(())
}
fn handle_event(&mut self, event: Event) {
log::info!("{:?}", event);
match event {
Event::MapRequest(event) => self.handle_map_request(event),
Event::ConfigureRequest(event) => self.handle_configure_request(event),
Event::ClientMessage(event) => self.handle_client_message(event),
Event::CreateNotify(event) => self.handle_create_notify(event),
Event::DestroyNotify(event) => self.handle_destroy_notify(event),
_ => { },
}
}
fn handle_destroy_notify(&mut self, event: DestroyNotifyEvent) {
let data = match self.windows.remove(&event.window) {
Some(w) => w,
_ => return,
};
if let Some(sid) = data.surface_id.take() {
self.windows_by_surface_id.remove(&sid);
}
if let Some(window) = data.window.take() {
window.destroy();
}
}
fn handle_create_notify(&mut self, event: CreateNotifyEvent) {
let data = Rc::new(XwindowData::new(&self.state, event.window, &self.client));
self.windows.insert(event.window, data);
}
fn handle_client_message(&mut self, event: ClientMessageEvent) {
if event.type_ == self.atoms.WL_SURFACE_ID {
self.handle_wl_surface_id(event);
}
}
fn handle_map_request(&mut self, event: MapRequestEvent) {
let res: Res<_> = try { self.c.map_window(event.window)?.check()? };
if let Err(e) = res {
log::error!("Could not map window: {}", ErrorFmt(&*e));
}
}
fn handle_configure_request(&mut self, event: ConfigureRequestEvent) {
let data = match self.windows.get(&event.window) {
Some(d) => d,
_ => return,
};
if let Some(w) = data.window.get() {
self.send_configure(w);
}
}
fn handle_wl_surface_id(&mut self, event: ClientMessageEvent) {
let data = match self.windows.get(&event.window) {
Some(d) => d.clone(),
_ => return,
};
if data.surface_id.get().is_some() {
log::error!("Surface id is already set");
return;
}
let [surface_id, ..] = event.data.as_data32();
let surface_id = WlSurfaceId::from_raw(surface_id);
data.surface_id.set(Some(surface_id));
self.windows_by_surface_id.insert(surface_id, data.clone());
if let Ok(surface) = self.client.lookup(surface_id) {
self.create_window(&data, surface);
}
}
}