From 7d28d306663f506778e9fbbb792cc3da7cd13946 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 24 Feb 2022 16:30:11 +0100 Subject: [PATCH] autocommit 2022-02-24 16:30:11 CET --- Cargo.lock | 54 +++ Cargo.toml | 1 + default-config/src/lib.rs | 7 +- src/acceptor.rs | 137 ++++--- src/client/mod.rs | 20 +- src/config/handler.rs | 2 +- src/forker.rs | 248 ++++++++++-- src/forker/io.rs | 16 +- src/globals.rs | 2 +- src/ifs/mod.rs | 2 +- src/ifs/wl_compositor.rs | 4 + src/ifs/wl_drm.rs | 81 +++- src/ifs/wl_output.rs | 13 +- src/ifs/wl_seat.rs | 10 +- src/ifs/wl_seat/event_handling.rs | 19 +- src/ifs/wl_seat/kb_owner.rs | 4 +- src/ifs/wl_surface.rs | 24 +- src/ifs/wl_surface/xdg_surface.rs | 6 - .../wl_surface/xdg_surface/xdg_toplevel.rs | 40 +- src/ifs/wl_surface/xwindow.rs | 247 ++++++++++++ src/ifs/wl_surface/zwlr_layer_surface_v1.rs | 65 ++-- src/ifs/zxdg_output_manager_v1.rs | 11 +- src/ifs/zxdg_output_v1.rs | 16 +- src/main.rs | 4 +- src/render/renderer/renderer.rs | 4 +- src/state.rs | 4 +- src/tree/mod.rs | 5 +- src/tree/output.rs | 12 +- src/tree/toplevel.rs | 19 + src/tree/walker.rs | 12 +- src/utils/bitflags.rs | 5 + src/utils/buffd/buf_in.rs | 6 +- src/utils/buffd/buf_out.rs | 6 +- src/utils/mod.rs | 1 + src/utils/queue.rs | 21 ++ src/utils/tri.rs | 49 +++ src/xwayland.rs | 245 ++++++++++++ src/xwayland/xsocket.rs | 103 +++++ src/xwayland/xwm.rs | 354 ++++++++++++++++++ 39 files changed, 1670 insertions(+), 209 deletions(-) create mode 100644 src/ifs/wl_surface/xwindow.rs create mode 100644 src/tree/toplevel.rs create mode 100644 src/utils/tri.rs create mode 100644 src/xwayland/xsocket.rs create mode 100644 src/xwayland/xwm.rs diff --git a/Cargo.lock b/Cargo.lock index ef45c76b..cba86dc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,16 @@ dependencies = [ "slab", ] +[[package]] +name = "gethostname" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4addc164932852d066774c405dbbdb7914742d2b39e39e1a7ca949c856d054d1" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getrandom" version = "0.2.4" @@ -425,6 +435,7 @@ dependencies = [ "smallvec", "thiserror", "uapi", + "x11rb", "xcb-dl", "xcb-dl-util", ] @@ -480,6 +491,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -490,6 +510,19 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -930,6 +963,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -945,6 +987,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "x11rb" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e99be55648b3ae2a52342f9a870c0e138709a3493261ce9b469afe6e4df6d8a" +dependencies = [ + "gethostname", + "nix", + "winapi", + "winapi-wsapoll", +] + [[package]] name = "xcb-dl" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index f184d697..67e0ca02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ cairo-rs = { version = "0.15.1", features = ["png"] } pango = { version = "0.15.2", features = ["v1_44"] } i4config = { path = "i4config" } default-config = { path = "default-config" } +x11rb = { version = "0.9.0", features = ["composite"] } [build-dependencies] repc = "0.1.1" diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index 11ecce9f..2dbc3380 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -1,9 +1,6 @@ use i4config::embedded::grab_keyboard; use i4config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT}; -use i4config::keyboard::syms::{ - SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_period, SYM_r, - SYM_t, SYM_v, SYM_y, -}; +use i4config::keyboard::syms::{SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_period, SYM_r, SYM_t, SYM_v, SYM_y, SYM_p}; use i4config::theme::{get_title_height, set_title_color, set_title_height, Color}; use i4config::Axis::{Horizontal, Vertical}; use i4config::Direction::{Down, Left, Right, Up}; @@ -69,6 +66,8 @@ fn configure_seat(s: Seat) { s.bind(SYM_Super_L, || Command::new("alacritty").spawn()); + s.bind(MOD | SYM_p, || Command::new("xeyes").spawn()); + fn do_grab(s: Seat, grab: bool) { for device in s.input_devices() { if let InputDevice::Keyboard(kb) = device { diff --git a/src/acceptor.rs b/src/acceptor.rs index 8603465a..a5908636 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -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, } -struct Unlinker(String); +struct AllocatedSocket { + // wayland-x + name: Ustring, + // /run/user/1000/wayland-x + path: Ustring, + fd: Rc, + // /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 { +fn bind_socket(fd: &Rc, xrd: &str, id: u32) -> Result { 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 { + 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) -> Result { - 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) -> Result { + 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, ) { diff --git a/src/client/mod.rs b/src/client/mod.rs index 555323cd..0f37eda7 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -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, + socket: OwnedFd, + uid: c::uid_t, + pid: c::pid_t, + xwayland_queue: Option>>, + ) -> Result, 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>, pub tracker: Tracker, + pub xwayland_queue: Option>>, } const MAX_PENDING_BUFFERS: usize = 10; diff --git a/src/config/handler.rs b/src/config/handler.rs index 8a2ac7ea..31cd0645 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -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(()) } diff --git a/src/forker.rs b/src/forker.rs index d4707b03..e8696323 100644 --- a/src/forker.rs +++ b/src/forker.rs @@ -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, @@ -29,6 +30,14 @@ pub struct ForkerProxy { task_out: Cell>>, task_proc: Cell>>, outgoing: AsyncQueue, + next_id: NumCell, + pending_pidfds: CopyHashMap>, + fds: RefCell>>, +} + +struct PidfdHandoff { + pidfd: Cell>>, + waiter: Cell>, } #[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, 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 { + 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, + dfd: Rc, + listenfd: Rc, + wmfd: Rc, + waylandfd: Rc, + ) -> Result { + 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, + env: Vec<(String, String)>, + stderr: Option>, + ) { + 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, 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, - val: Vec, + val: Option>, }, Spawn { prog: String, args: Vec, 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, + fds: RefCell>>, outgoing: AsyncQueue, pending_spawns: CopyHashMap>, } @@ -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, msg: ServerMessage) { + fn handle_msg(self: &Rc, 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, var: &[u8], val: &[u8]) { - env::set_var(OsStr::from_bytes(var), OsStr::from_bytes(val)); + fn handle_set_env(self: &Rc, var: &[u8], val: Option>) { + 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, prog: String, args: Vec, env: Vec<(String, String)>) { + fn handle_xwayland(self: &Rc, 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, + prog: String, + args: Vec, + 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, + prog: String, + args: Vec, + env: Vec<(String, String)>, + stderr: Option, + fds: Vec, + pidfd_id: Option, + ) { 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(); diff --git a/src/forker/io.rs b/src/forker/io.rs index f70f5257..4da9fb11 100644 --- a/src/forker/io.rs +++ b/src/forker/io.rs @@ -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, - fds: Vec> + fds: Vec>, } impl IoOut { @@ -59,7 +59,7 @@ impl IoOut { Self { outgoing: BufFdOut::new(fd), scratch: vec![], - fds: vec![] + fds: vec![], } } diff --git a/src/globals.rs b/src/globals.rs index 29b48848..5621bab6 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -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 { diff --git a/src/ifs/mod.rs b/src/ifs/mod.rs index 80317384..0d61c7f3 100644 --- a/src/ifs/mod.rs +++ b/src/ifs/mod.rs @@ -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; diff --git a/src/ifs/wl_compositor.rs b/src/ifs/wl_compositor.rs index 0875054e..75633af0 100644 --- a/src/ifs/wl_compositor.rs +++ b/src/ifs/wl_compositor.rs @@ -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(()) } diff --git a/src/ifs/wl_drm.rs b/src/ifs/wl_drm.rs index b62a8374..4984f879 100644 --- a/src/ifs/wl_drm.rs +++ b/src/ifs/wl_drm.rs @@ -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, + 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), } @@ -157,3 +218,19 @@ pub enum CreatePlanarBufferError { Unsupported, } efrom!(CreatePlanarBufferError, ParseError, MsgParserError); + +#[derive(Debug, Error)] +pub enum CreatePrimeBufferError { + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[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), +} +efrom!(CreatePrimeBufferError, MsgParserError); +efrom!(CreatePrimeBufferError, ClientError); diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index 3816b5f3..8c6f14f4 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -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() { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 0ebe9867..54a46cb1 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -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>>, found_tree: RefCell>, - toplevel_focus_history: LinkedList>, + toplevel_focus_history: LinkedList>, keyboard_node: CloneCell>, pressed_keys: RefCell>, bindings: RefCell>>>, diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 8f2d484b..b3640910 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -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> { + pub fn last_tiled_keyboard_toplevel(&self, new: &dyn Node) -> Option> { 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, n: &Rc) { + pub fn focus_toplevel(self: &Rc, n: Rc) { 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, xdg: &Rc) { - 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) { @@ -415,7 +410,7 @@ impl WlSeatGlobal { // Enter callbacks impl WlSeatGlobal { - pub fn enter_toplevel(self: &Rc, n: &Rc) { + pub fn enter_toplevel(self: &Rc, n: Rc) { self.focus_toplevel(n); } diff --git a/src/ifs/wl_seat/kb_owner.rs b/src/ifs/wl_seat/kb_owner.rs index 547de118..e50432bb 100644 --- a/src/ifs/wl_seat/kb_owner.rs +++ b/src/ifs/wl_seat/kb_owner.rs @@ -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, diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 9b6806b4..778434f2 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -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), #[error(transparent)] ClientError(Box), - #[error("Cannot destroy a `wl_surface` before its role object")] - ReloObjectStillExists, + #[error(transparent)] + WlSurfaceError(Box), } efrom!(DestroyError, ParseFailed, MsgParserError); efrom!(DestroyError, ClientError); +efrom!(DestroyError, WlSurfaceError); #[derive(Debug, Error)] pub enum AttachError { diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index a3381abd..5ca0231a 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -231,12 +231,6 @@ impl XdgSurface { } } - pub fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc { - 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); diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index 7c847f5c..25695821 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -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>>, pub children: RefCell>>, states: RefCell>, - pub toplevel_history: SmallMap>, 1>, + pub toplevel_history: SmallMap>, 1>, active_surfaces: NumCell, pub decoration: Cell, 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, seat: &Rc, _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, seat: &Rc, _x: Fixed, _y: Fixed) { - seat.enter_toplevel(&self); + seat.enter_toplevel(self); } fn pointer_focus(&self, seat: &Rc) { @@ -478,6 +473,27 @@ impl Node for XdgToplevel { } } +impl ToplevelNode for XdgToplevel { + fn parent(&self) -> Option> { + self.parent_node.get() + } + + fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc { + self.xdg + .focus_surface + .get(&seat.id()) + .unwrap_or_else(|| self.xdg.surface.clone()) + } + + fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode>) { + self.toplevel_history.insert(seat.id(), link); + } + + fn as_node(&self) -> &dyn Node { + self + } +} + impl XdgSurfaceExt for XdgToplevel { fn focus_parent(&self, seat: &Rc) { 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(); diff --git a/src/ifs/wl_surface/xwindow.rs b/src/ifs/wl_surface/xwindow.rs new file mode 100644 index 00000000..78813dd5 --- /dev/null +++ b/src/ifs/wl_surface/xwindow.rs @@ -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, + pub window_id: Window, + pub client: Rc, + pub surface_id: Cell>, + pub window: CloneCell>>, +} + +tree_id!(XwindowId); +pub struct Xwindow { + pub id: XwindowId, + pub seat_state: NodeSeatState, + pub data: Rc, + pub surface: Rc, + pub parent: CloneCell>>, + pub focus_history: SmallMap>, 1>, + pub events: Rc>, + pub extents: Cell, + pub workspace: CloneCell>>, +} + +impl XwindowData { + pub fn new(state: &Rc, window_id: Window, client: &Rc) -> 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, surface: &Rc, events: &Rc>) -> 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) -> 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) { + 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, 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, seat: &Rc, _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) -> 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, seat: &Rc, _x: Fixed, _y: Fixed) { + seat.enter_toplevel(self); + } + + fn pointer_focus(&self, seat: &Rc) { + 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, 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, ws: &Rc) { + self.workspace.set(Some(ws.clone())); + } + + fn set_parent(self: Rc, parent: Rc) { + self.parent.set(Some(parent)); + self.notify_parent(); + } + + fn client(&self) -> Option> { + Some(self.data.client.clone()) + } +} + +impl ToplevelNode for Xwindow { + fn parent(&self) -> Option> { + self.parent.get() + } + + fn focus_surface(&self, _seat: &WlSeatGlobal) -> Rc { + self.surface.clone() + } + + fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode>) { + 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), +} diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index 25e9dad8..6d36dac4 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -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) } diff --git a/src/ifs/zxdg_output_manager_v1.rs b/src/ifs/zxdg_output_manager_v1.rs index 7a3cee9a..9ea700ae 100644 --- a/src/ifs/zxdg_output_manager_v1.rs +++ b/src/ifs/zxdg_output_manager_v1.rs @@ -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, - parser: MsgParser<'_, '_>, - ) -> Result<(), GetXdgOutputError> { + fn get_xdg_output(self: &Rc, 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)?; diff --git a/src/ifs/zxdg_output_v1.rs b/src/ifs/zxdg_output_v1.rs index 6bb2a3ab..11f0b188 100644 --- a/src/ifs/zxdg_output_v1.rs +++ b/src/ifs/zxdg_output_v1.rs @@ -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, diff --git a/src/main.rs b/src/main.rs index 32fe2f11..33686dcc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(); diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 2f19a203..c7eb6399 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -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]); diff --git a/src/state.rs b/src/state.rs index c3feada2..909be635 100644 --- a/src/state.rs +++ b/src/state.rs @@ -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; } } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 7c5b859f..fc846f63 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -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, diff --git a/src/tree/output.rs b/src/tree/output.rs index d11dee73..5eead285 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -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 { diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs new file mode 100644 index 00000000..fa010100 --- /dev/null +++ b/src/tree/toplevel.rs @@ -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>; + fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc; + fn set_focus_history_link(&self, seat: &WlSeatGlobal, link: LinkedNode>); + fn as_node(&self) -> &dyn Node; + + fn parent_is_float(&self) -> bool { + if let Some(parent) = self.parent() { + return parent.is_float(); + } + false + } +} diff --git a/src/tree/walker.rs b/src/tree/walker.rs index 52d04923..586138b7 100644 --- a/src/tree/walker.rs +++ b/src/tree/walker.rs @@ -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) { @@ -42,6 +43,10 @@ pub trait NodeVisitorBase: Sized { fn visit_layer_surface(&mut self, node: &Rc) { node.visit_children(self); } + + fn visit_xwindow(&mut self, node: &Rc) { + node.visit_children(self); + } } pub trait NodeVisitor { @@ -54,6 +59,7 @@ pub trait NodeVisitor { fn visit_float(&mut self, node: &Rc); fn visit_workspace(&mut self, node: &Rc); fn visit_layer_surface(&mut self, node: &Rc); + fn visit_xwindow(&mut self, node: &Rc); } impl NodeVisitor for T { @@ -92,6 +98,10 @@ impl NodeVisitor for T { fn visit_layer_surface(&mut self, node: &Rc) { ::visit_layer_surface(self, node) } + + fn visit_xwindow(&mut self, node: &Rc) { + ::visit_xwindow(self, node) + } } // pub fn visit_containers)>(f: F) -> impl NodeVisitor { diff --git a/src/utils/bitflags.rs b/src/utils/bitflags.rs index 9ab2f653..39c80f50 100644 --- a/src/utils/bitflags.rs +++ b/src/utils/bitflags.rs @@ -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 } } diff --git a/src/utils/buffd/buf_in.rs b/src/utils/buffd/buf_in.rs index 27727c30..e350ea96 100644 --- a/src/utils/buffd/buf_in.rs +++ b/src/utils/buffd/buf_in.rs @@ -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), diff --git a/src/utils/buffd/buf_out.rs b/src/utils/buffd/buf_out.rs index 7a66dc44..3d3f7b1a 100644 --- a/src/utils/buffd/buf_out.rs +++ b/src/utils/buffd/buf_out.rs @@ -157,7 +157,11 @@ impl BufFdOut { Ok(false) } - pub async fn flush2(&mut self, buf: &[u8], fds: &mut Vec>) -> Result<(), BufFdError> { + pub async fn flush2( + &mut self, + buf: &[u8], + fds: &mut Vec>, + ) -> Result<(), BufFdError> { let mut read_pos = 0; while read_pos < buf.len() { if self.flush_sync2(&mut read_pos, buf, fds)? { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index bcaf9c6b..7e934fc4 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -12,4 +12,5 @@ pub mod ptr_ext; pub mod queue; pub mod smallmap; pub mod stack; +pub mod tri; pub mod vec_ext; diff --git a/src/utils/queue.rs b/src/utils/queue.rs index 9a3575b9..d9ab7d92 100644 --- a/src/utils/queue.rs +++ b/src/utils/queue.rs @@ -39,6 +39,10 @@ impl AsyncQueue { 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, +} + +impl<'a, T> Future for AsyncQueueNonEmpty<'a, T> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.queue.data.borrow_mut().len() > 0 { + Poll::Ready(()) + } else { + self.queue.waiter.set(Some(cx.waker().clone())); + Poll::Pending + } + } +} diff --git a/src/utils/tri.rs b/src/utils/tri.rs new file mode 100644 index 00000000..353a05cc --- /dev/null +++ b/src/utils/tri.rs @@ -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) -> Result<(), Self> + where + F: FnOnce() -> Result<(), Self>; + + fn tria(f: F) -> Tria + where + F: Future>; +} + +impl Try for E { + fn tri(f: F) -> Result<(), Self> + where + F: FnOnce() -> Result<(), Self>, + { + f() + } + + fn tria(f: F) -> Tria + where + F: Future>, + { + Tria { + f, + _phantom: Default::default(), + } + } +} + +pub struct Tria { + f: F, + _phantom: PhantomData, +} + +impl Future for Tria +where + F: Future>, +{ + type Output = Result<(), E>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { Pin::new_unchecked(&mut Pin::get_unchecked_mut(self).f).poll(cx) } + } +} diff --git a/src/xwayland.rs b/src/xwayland.rs index e69de29b..3a03501a 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -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), + #[error("Could not connect to Xwayland")] + Connect(#[source] Box), + #[error("Could not create a window manager")] + CreateWm(#[source] Box), + #[error("Could not select the root events")] + SelectRootEvents(#[source] Box), + #[error("Could not create the WM window")] + CreateXWindow(#[source] Box), + #[error("Could not acquire a selection")] + SelectionOwner(#[source] Box), + #[error("composite_redirect_subwindows failed")] + CompositeRedirectSubwindows(#[source] Box), + #[error("Could not spawn the Xwayland client")] + SpawnClient(#[source] ClientError), +} + +pub async fn manage(state: Rc) { + 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, + forker: &Rc, + socket: Rc, +) -> 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) { + 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, 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), + SurfaceDestroyed(WlSurfaceId), + Configure(Rc), +} diff --git a/src/xwayland/xsocket.rs b/src/xwayland/xsocket.rs new file mode 100644 index 00000000..4236dbdb --- /dev/null +++ b/src/xwayland/xsocket.rs @@ -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, id: u32) -> Result<(XSocket, Rc), 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), 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) +} diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs new file mode 100644 index 00000000..6219e3ec --- /dev/null +++ b/src/xwayland/xwm.rs @@ -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 = Result>; + +pub struct Wm { + state: Rc, + c: RustConnection, + atoms: Atoms, + socket: AsyncFd, + root: Window, + xwin: Window, + client: Rc, + windows: AHashMap>, + windows_by_surface_id: AHashMap>, + queue: Rc>, +} + +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, client: Rc, socket: OwnedFd, queue: Rc>) -> Result { + 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) { + self.send_configure(window); + } + + fn send_configure(&mut self, window: Rc) { + 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, surface: Rc) { + 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) { + 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); + } + } +}