autocommit 2022-02-24 16:30:11 CET
This commit is contained in:
parent
666e475032
commit
7d28d30666
39 changed files with 1670 additions and 209 deletions
137
src/acceptor.rs
137
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<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,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
248
src/forker.rs
248
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<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();
|
||||
|
|
|
|||
|
|
@ -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![],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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>>>>,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
247
src/ifs/wl_surface/xwindow.rs
Normal file
247
src/ifs/wl_surface/xwindow.rs
Normal 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),
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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
19
src/tree/toplevel.rs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)? {
|
||||
|
|
|
|||
|
|
@ -12,4 +12,5 @@ pub mod ptr_ext;
|
|||
pub mod queue;
|
||||
pub mod smallmap;
|
||||
pub mod stack;
|
||||
pub mod tri;
|
||||
pub mod vec_ext;
|
||||
|
|
|
|||
|
|
@ -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
49
src/utils/tri.rs
Normal 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) }
|
||||
}
|
||||
}
|
||||
245
src/xwayland.rs
245
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<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
103
src/xwayland/xsocket.rs
Normal 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
354
src/xwayland/xwm.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue