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
54
Cargo.lock
generated
54
Cargo.lock
generated
|
|
@ -300,6 +300,16 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gethostname"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4addc164932852d066774c405dbbdb7914742d2b39e39e1a7ca949c856d054d1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
|
@ -425,6 +435,7 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uapi",
|
"uapi",
|
||||||
|
"x11rb",
|
||||||
"xcb-dl",
|
"xcb-dl",
|
||||||
"xcb-dl-util",
|
"xcb-dl-util",
|
||||||
]
|
]
|
||||||
|
|
@ -480,6 +491,15 @@ version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
|
@ -490,6 +510,19 @@ dependencies = [
|
||||||
"autocfg",
|
"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]]
|
[[package]]
|
||||||
name = "num-derive"
|
name = "num-derive"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|
@ -930,6 +963,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-wsapoll"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
@ -945,6 +987,18 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "xcb-dl"
|
name = "xcb-dl"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ cairo-rs = { version = "0.15.1", features = ["png"] }
|
||||||
pango = { version = "0.15.2", features = ["v1_44"] }
|
pango = { version = "0.15.2", features = ["v1_44"] }
|
||||||
i4config = { path = "i4config" }
|
i4config = { path = "i4config" }
|
||||||
default-config = { path = "default-config" }
|
default-config = { path = "default-config" }
|
||||||
|
x11rb = { version = "0.9.0", features = ["composite"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
repc = "0.1.1"
|
repc = "0.1.1"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
use i4config::embedded::grab_keyboard;
|
use i4config::embedded::grab_keyboard;
|
||||||
use i4config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT};
|
use i4config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT};
|
||||||
use i4config::keyboard::syms::{
|
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};
|
||||||
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::theme::{get_title_height, set_title_color, set_title_height, Color};
|
use i4config::theme::{get_title_height, set_title_color, set_title_height, Color};
|
||||||
use i4config::Axis::{Horizontal, Vertical};
|
use i4config::Axis::{Horizontal, Vertical};
|
||||||
use i4config::Direction::{Down, Left, Right, Up};
|
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(SYM_Super_L, || Command::new("alacritty").spawn());
|
||||||
|
|
||||||
|
s.bind(MOD | SYM_p, || Command::new("xeyes").spawn());
|
||||||
|
|
||||||
fn do_grab(s: Seat, grab: bool) {
|
fn do_grab(s: Seat, grab: bool) {
|
||||||
for device in s.input_devices() {
|
for device in s.input_devices() {
|
||||||
if let InputDevice::Keyboard(kb) = device {
|
if let InputDevice::Keyboard(kb) = device {
|
||||||
|
|
|
||||||
137
src/acceptor.rs
137
src/acceptor.rs
|
|
@ -1,20 +1,27 @@
|
||||||
use crate::client::ClientError;
|
use crate::client::ClientError;
|
||||||
use crate::event_loop::{EventLoopDispatcher, EventLoopError, EventLoopId};
|
use crate::event_loop::{EventLoopDispatcher, EventLoopError, EventLoopId};
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
use crate::ErrorFmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uapi::{c, Errno, OwnedFd};
|
use uapi::{c, format_ustr, Errno, OwnedFd, Ustring};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum AcceptorError {
|
pub enum AcceptorError {
|
||||||
#[error("XDG_RUNTIME_DIR is not set")]
|
#[error("XDG_RUNTIME_DIR is not set")]
|
||||||
XrdNotSet,
|
XrdNotSet,
|
||||||
#[error("XDG_RUNTIME_DIR is too long to form a unix socket address")]
|
#[error("XDG_RUNTIME_DIR ({0:?}) is too long to form a unix socket address")]
|
||||||
XrdTooLong,
|
XrdTooLong(String),
|
||||||
#[error("Could not create a wayland socket")]
|
#[error("Could not create a wayland socket")]
|
||||||
SocketFailed(#[source] std::io::Error),
|
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")]
|
#[error("Could not start listening for incoming connections")]
|
||||||
ListenFailed(#[source] std::io::Error),
|
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")]
|
#[error("The wayland socket is in an error state")]
|
||||||
ErrorEvent,
|
ErrorEvent,
|
||||||
#[error("Could not accept new connections")]
|
#[error("Could not accept new connections")]
|
||||||
|
|
@ -30,76 +37,110 @@ pub enum AcceptorError {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Acceptor {
|
pub struct Acceptor {
|
||||||
_unlinker: Unlinker,
|
|
||||||
id: EventLoopId,
|
id: EventLoopId,
|
||||||
fd: OwnedFd,
|
socket: AllocatedSocket,
|
||||||
global: Rc<State>,
|
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) {
|
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 {
|
fn bind_socket(fd: &Rc<OwnedFd>, xrd: &str, id: u32) -> Result<AllocatedSocket, AcceptorError> {
|
||||||
format!("{}/wayland-{}", xrd, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bind_socket(fd: i32, xdr: &str) -> Result<u32, AcceptorError> {
|
|
||||||
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
|
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
|
||||||
addr.sun_family = c::AF_UNIX as _;
|
addr.sun_family = c::AF_UNIX as _;
|
||||||
for i in 0..1000 {
|
let name = format_ustr!("wayland-{}", id);
|
||||||
let path = socket_path(xdr, i);
|
let path = format_ustr!("{}/{}", xrd, name.display());
|
||||||
if path.len() + 1 > addr.sun_path.len() {
|
let lock_path = format_ustr!("{}.lock", path.display());
|
||||||
return Err(AcceptorError::XrdTooLong);
|
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[..]);
|
Err(Errno(c::ENOENT)) => {}
|
||||||
sun_path[..path.len()].copy_from_slice(path.as_bytes());
|
Err(e) => return Err(AcceptorError::SocketStat(e.into())),
|
||||||
sun_path[path.len()] = 0;
|
}
|
||||||
match uapi::bind(fd, &addr) {
|
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
|
||||||
Ok(()) => return Ok(i),
|
sun_path[..path.len()].copy_from_slice(path.as_bytes());
|
||||||
Err(Errno(c::EADDRINUSE)) => {
|
sun_path[path.len()] = 0;
|
||||||
log::warn!("Socket {} is already in use", path);
|
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)
|
Err(AcceptorError::AddressesInUse)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Acceptor {
|
impl Acceptor {
|
||||||
pub fn install(global: &Rc<State>) -> Result<String, AcceptorError> {
|
pub fn install(global: &Rc<State>) -> Result<Ustring, AcceptorError> {
|
||||||
let xrd = match std::env::var("XDG_RUNTIME_DIR") {
|
let socket = allocate_socket()?;
|
||||||
Ok(d) => d,
|
log::info!("bound to socket {}", socket.path.display());
|
||||||
Err(_) => return Err(AcceptorError::XrdNotSet),
|
if let Err(e) = uapi::listen(socket.fd.raw(), 4096) {
|
||||||
};
|
|
||||||
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) {
|
|
||||||
return Err(AcceptorError::ListenFailed(e.into()));
|
return Err(AcceptorError::ListenFailed(e.into()));
|
||||||
}
|
}
|
||||||
let id = global.el.id();
|
let id = global.el.id();
|
||||||
|
let name = socket.name.to_owned();
|
||||||
let acc = Rc::new(Acceptor {
|
let acc = Rc::new(Acceptor {
|
||||||
_unlinker: unlinker,
|
|
||||||
id,
|
id,
|
||||||
fd,
|
socket,
|
||||||
global: global.clone(),
|
global: global.clone(),
|
||||||
});
|
});
|
||||||
global.el.insert(id, Some(acc.fd.raw()), c::EPOLLIN, acc)?;
|
global
|
||||||
Ok(socket_path)
|
.el
|
||||||
|
.insert(id, Some(acc.socket.fd.raw()), c::EPOLLIN, acc)?;
|
||||||
|
Ok(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,7 +151,7 @@ impl EventLoopDispatcher for Acceptor {
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let fd = match uapi::accept4(
|
let fd = match uapi::accept4(
|
||||||
self.fd.raw(),
|
self.socket.fd.raw(),
|
||||||
uapi::sockaddr_none_mut(),
|
uapi::sockaddr_none_mut(),
|
||||||
c::SOCK_NONBLOCK | c::SOCK_CLOEXEC,
|
c::SOCK_NONBLOCK | c::SOCK_CLOEXEC,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ use std::mem;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use uapi::{c, OwnedFd};
|
use uapi::{c, OwnedFd};
|
||||||
|
use crate::xwayland::XWaylandEvent;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod objects;
|
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 {
|
let data = Rc::new(Client {
|
||||||
id,
|
id,
|
||||||
state: global.clone(),
|
state: global.clone(),
|
||||||
|
|
@ -103,6 +117,7 @@ impl Clients {
|
||||||
shutdown: Default::default(),
|
shutdown: Default::default(),
|
||||||
dispatch_frame_requests: AsyncQueue::new(),
|
dispatch_frame_requests: AsyncQueue::new(),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
xwayland_queue,
|
||||||
});
|
});
|
||||||
track!(data, data);
|
track!(data, data);
|
||||||
let display = Rc::new(WlDisplay::new(&data));
|
let display = Rc::new(WlDisplay::new(&data));
|
||||||
|
|
@ -111,7 +126,7 @@ impl Clients {
|
||||||
data.objects.add_client_object(display).expect("");
|
data.objects.add_client_object(display).expect("");
|
||||||
let client = ClientHolder {
|
let client = ClientHolder {
|
||||||
_handler: global.eng.spawn(tasks::client(data.clone())),
|
_handler: global.eng.spawn(tasks::client(data.clone())),
|
||||||
data,
|
data: data.clone(),
|
||||||
};
|
};
|
||||||
log::info!(
|
log::info!(
|
||||||
"Client {} connected, pid: {}, uid: {}, fd: {}",
|
"Client {} connected, pid: {}, uid: {}, fd: {}",
|
||||||
|
|
@ -121,7 +136,7 @@ impl Clients {
|
||||||
client.data.socket.raw()
|
client.data.socket.raw()
|
||||||
);
|
);
|
||||||
self.clients.borrow_mut().insert(client.data.id, client);
|
self.clients.borrow_mut().insert(client.data.id, client);
|
||||||
Ok(())
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kill(&self, client: ClientId) {
|
pub fn kill(&self, client: ClientId) {
|
||||||
|
|
@ -193,6 +208,7 @@ pub struct Client {
|
||||||
shutdown: AsyncEvent,
|
shutdown: AsyncEvent,
|
||||||
pub dispatch_frame_requests: AsyncQueue<Rc<WlCallback>>,
|
pub dispatch_frame_requests: AsyncQueue<Rc<WlCallback>>,
|
||||||
pub tracker: Tracker<Client>,
|
pub tracker: Tracker<Client>,
|
||||||
|
pub xwayland_queue: Option<Rc<AsyncQueue<XWaylandEvent>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_PENDING_BUFFERS: usize = 10;
|
const MAX_PENDING_BUFFERS: usize = 10;
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ impl ConfigProxyHandler {
|
||||||
Some(f) => f,
|
Some(f) => f,
|
||||||
_ => return Err(RunError::NoForker),
|
_ => return Err(RunError::NoForker),
|
||||||
};
|
};
|
||||||
forker.spawn(prog.to_string(), args, env);
|
forker.spawn(prog.to_string(), args, env, None);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
248
src/forker.rs
248
src/forker.rs
|
|
@ -3,23 +3,24 @@ mod io;
|
||||||
|
|
||||||
use crate::async_engine::{AsyncFd, SpawnedFuture};
|
use crate::async_engine::{AsyncFd, SpawnedFuture};
|
||||||
use crate::forker::clone3::{fork_with_pidfd, Forked};
|
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::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 bincode::{Decode, Encode};
|
||||||
use i4config::_private::bincode_ops;
|
use i4config::_private::bincode_ops;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
use bincode::error::{DecodeError, EncodeError};
|
use std::task::{Poll, Waker};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uapi::{c, pipe2, Fd, IntoUstr, OwnedFd, UstrPtr};
|
use uapi::{c, pipe2, Errno, Fd, IntoUstr, OwnedFd, UstrPtr};
|
||||||
use crate::forker::io::{IoIn, IoOut};
|
|
||||||
|
|
||||||
pub struct ForkerProxy {
|
pub struct ForkerProxy {
|
||||||
pidfd: Rc<OwnedFd>,
|
pidfd: Rc<OwnedFd>,
|
||||||
|
|
@ -29,6 +30,14 @@ pub struct ForkerProxy {
|
||||||
task_out: Cell<Option<SpawnedFuture<()>>>,
|
task_out: Cell<Option<SpawnedFuture<()>>>,
|
||||||
task_proc: Cell<Option<SpawnedFuture<()>>>,
|
task_proc: Cell<Option<SpawnedFuture<()>>>,
|
||||||
outgoing: AsyncQueue<ServerMessage>,
|
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)]
|
#[derive(Debug, Error)]
|
||||||
|
|
@ -45,6 +54,8 @@ pub enum ForkerError {
|
||||||
DecodeFailed(#[source] DecodeError),
|
DecodeFailed(#[source] DecodeError),
|
||||||
#[error("Could not encode the next message")]
|
#[error("Could not encode the next message")]
|
||||||
EncodeFailed(#[source] EncodeError),
|
EncodeFailed(#[source] EncodeError),
|
||||||
|
#[error("Could not fork")]
|
||||||
|
PidfdForkFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ForkerProxy {
|
impl ForkerProxy {
|
||||||
|
|
@ -67,6 +78,9 @@ impl ForkerProxy {
|
||||||
task_out: Cell::new(None),
|
task_out: Cell::new(None),
|
||||||
task_proc: Cell::new(None),
|
task_proc: Cell::new(None),
|
||||||
outgoing: Default::default(),
|
outgoing: Default::default(),
|
||||||
|
next_id: Default::default(),
|
||||||
|
pending_pidfds: Default::default(),
|
||||||
|
fds: Default::default(),
|
||||||
}),
|
}),
|
||||||
Forked::Child { .. } => Forker::handle(pid, child),
|
Forked::Child { .. } => Forker::handle(pid, child),
|
||||||
}
|
}
|
||||||
|
|
@ -90,12 +104,65 @@ impl ForkerProxy {
|
||||||
pub fn setenv(&self, key: &[u8], val: &[u8]) {
|
pub fn setenv(&self, key: &[u8], val: &[u8]) {
|
||||||
self.outgoing.push(ServerMessage::SetEnv {
|
self.outgoing.push(ServerMessage::SetEnv {
|
||||||
var: key.to_vec(),
|
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)>) {
|
pub fn unsetenv(&self, key: &[u8]) {
|
||||||
self.outgoing.push(ServerMessage::Spawn { prog, args, env })
|
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) {
|
async fn incoming(self: Rc<Self>, socket: AsyncFd) {
|
||||||
|
|
@ -109,13 +176,29 @@ impl ForkerProxy {
|
||||||
return;
|
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 {
|
match msg {
|
||||||
ForkerMessage::Log { level, msg } => self.handle_log(level, &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);
|
let mut io = IoOut::new(socket);
|
||||||
loop {
|
loop {
|
||||||
let msg = self.outgoing.pop().await;
|
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 {
|
if let Err(e) = io.write_msg(msg).await {
|
||||||
log::error!("Could not write to the ol' forker: {}", ErrorFmt(e));
|
log::error!("Could not write to the ol' forker: {}", ErrorFmt(e));
|
||||||
state.forker.set(None);
|
state.forker.set(None);
|
||||||
|
|
@ -159,23 +245,29 @@ impl ForkerProxy {
|
||||||
enum ServerMessage {
|
enum ServerMessage {
|
||||||
SetEnv {
|
SetEnv {
|
||||||
var: Vec<u8>,
|
var: Vec<u8>,
|
||||||
val: Vec<u8>,
|
val: Option<Vec<u8>>,
|
||||||
},
|
},
|
||||||
Spawn {
|
Spawn {
|
||||||
prog: String,
|
prog: String,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
env: Vec<(String, String)>,
|
env: Vec<(String, String)>,
|
||||||
|
stderr: bool,
|
||||||
|
},
|
||||||
|
Xwayland {
|
||||||
|
id: u32,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Encode, Decode)]
|
#[derive(Encode, Decode)]
|
||||||
enum ForkerMessage {
|
enum ForkerMessage {
|
||||||
Log { level: usize, msg: String },
|
Log { level: usize, msg: String },
|
||||||
|
PidFd { id: u32, success: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Forker {
|
struct Forker {
|
||||||
socket: AsyncFd,
|
socket: AsyncFd,
|
||||||
ae: Rc<AsyncEngine>,
|
ae: Rc<AsyncEngine>,
|
||||||
|
fds: RefCell<Vec<Rc<OwnedFd>>>,
|
||||||
outgoing: AsyncQueue<ForkerMessage>,
|
outgoing: AsyncQueue<ForkerMessage>,
|
||||||
pending_spawns: CopyHashMap<c::pid_t, SpawnedFuture<()>>,
|
pending_spawns: CopyHashMap<c::pid_t, SpawnedFuture<()>>,
|
||||||
}
|
}
|
||||||
|
|
@ -206,6 +298,7 @@ impl Forker {
|
||||||
let forker = Rc::new(Forker {
|
let forker = Rc::new(Forker {
|
||||||
socket: ae.fd(&socket).unwrap(),
|
socket: ae.fd(&socket).unwrap(),
|
||||||
ae: ae.clone(),
|
ae: ae.clone(),
|
||||||
|
fds: RefCell::new(vec![]),
|
||||||
outgoing: Default::default(),
|
outgoing: Default::default(),
|
||||||
pending_spawns: Default::default(),
|
pending_spawns: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
@ -219,6 +312,9 @@ impl Forker {
|
||||||
let mut io = IoOut::new(self.socket.clone());
|
let mut io = IoOut::new(self.socket.clone());
|
||||||
loop {
|
loop {
|
||||||
let msg = self.outgoing.pop().await;
|
let msg = self.outgoing.pop().await;
|
||||||
|
for fd in self.fds.borrow_mut().drain(..) {
|
||||||
|
io.push_fd(fd);
|
||||||
|
}
|
||||||
io.write_msg(msg).await.unwrap();
|
io.write_msg(msg).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -227,26 +323,76 @@ impl Forker {
|
||||||
let mut io = IoIn::new(self.socket.clone());
|
let mut io = IoIn::new(self.socket.clone());
|
||||||
loop {
|
loop {
|
||||||
let msg = io.read_msg().await.unwrap();
|
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 {
|
match msg {
|
||||||
ServerMessage::SetEnv { var, val } => self.handle_set_env(&var, &val),
|
ServerMessage::SetEnv { var, val } => self.handle_set_env(&var, val),
|
||||||
ServerMessage::Spawn { prog, args, env } => self.handle_spawn(prog, args, env),
|
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]) {
|
fn handle_set_env(self: &Rc<Self>, var: &[u8], val: Option<Vec<u8>>) {
|
||||||
env::set_var(OsStr::from_bytes(var), OsStr::from_bytes(val));
|
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 (read, mut write) = pipe2(c::O_CLOEXEC).unwrap();
|
||||||
let res = match fork_with_pidfd(false) {
|
let res = match fork_with_pidfd(false) {
|
||||||
Ok(o) => o,
|
Ok(o) => o,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
if let Some(id) = pidfd_id {
|
||||||
|
self.outgoing
|
||||||
|
.push(ForkerMessage::PidFd { id, success: false });
|
||||||
|
}
|
||||||
self.outgoing.push(ForkerMessage::Log {
|
self.outgoing.push(ForkerMessage::Log {
|
||||||
level: log::Level::Error as usize,
|
level: log::Level::Error as usize,
|
||||||
msg: ErrorFmt(e).to_string(),
|
msg: ErrorFmt(e).to_string(),
|
||||||
|
|
@ -255,7 +401,12 @@ impl Forker {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match res {
|
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);
|
drop(write);
|
||||||
let slf = self.clone();
|
let slf = self.clone();
|
||||||
let spawn = self.ae.spawn(async move {
|
let spawn = self.ae.spawn(async move {
|
||||||
|
|
@ -274,20 +425,39 @@ impl Forker {
|
||||||
self.pending_spawns.set(pid, spawn);
|
self.pending_spawns.set(pid, spawn);
|
||||||
}
|
}
|
||||||
Forked::Child { .. } => {
|
Forked::Child { .. } => {
|
||||||
unsafe {
|
let err = (|| {
|
||||||
c::signal(c::SIGCHLD, c::SIG_DFL);
|
if let Some(stderr) = stderr {
|
||||||
}
|
uapi::dup2(stderr.raw(), 2).unwrap();
|
||||||
for (key, val) in env {
|
}
|
||||||
env::set_var(&key, &val);
|
for fd in fds {
|
||||||
}
|
let fd = fd.unwrap();
|
||||||
let prog = prog.into_ustr();
|
let res: Result<_, Errno> = (|| {
|
||||||
let mut argsnt = UstrPtr::new();
|
uapi::fcntl_setfd(fd, uapi::fcntl_getfd(fd)? & !c::FD_CLOEXEC)?;
|
||||||
argsnt.push(&prog);
|
Ok(())
|
||||||
for arg in args {
|
})();
|
||||||
argsnt.push(arg);
|
if let Err(e) = res {
|
||||||
}
|
return Err(SpawnError::Cloexec(e.into()));
|
||||||
if let Err(e) = uapi::execvp(&prog, &argsnt) {
|
}
|
||||||
let _ = write.write_all(std::io::Error::from(e).to_string().as_bytes());
|
}
|
||||||
|
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);
|
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 {
|
fn setup_fds(mut socket: OwnedFd) -> OwnedFd {
|
||||||
if socket.raw() != 0 {
|
if socket.raw() != 0 {
|
||||||
uapi::dup3(socket.unwrap(), 0, 0).unwrap();
|
uapi::dup3(socket.unwrap(), 0, 0).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
|
use bincode::{Decode, Encode};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
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::async_engine::AsyncFd;
|
||||||
use crate::ForkerError;
|
|
||||||
use crate::utils::buffd::{BufFdIn, BufFdOut};
|
use crate::utils::buffd::{BufFdIn, BufFdOut};
|
||||||
use crate::utils::vec_ext::VecExt;
|
use crate::utils::vec_ext::VecExt;
|
||||||
|
use crate::ForkerError;
|
||||||
|
use i4config::_private::bincode_ops;
|
||||||
|
use uapi::OwnedFd;
|
||||||
|
|
||||||
pub struct IoIn {
|
pub struct IoIn {
|
||||||
incoming: BufFdIn,
|
incoming: BufFdIn,
|
||||||
|
|
@ -18,7 +18,7 @@ impl IoIn {
|
||||||
pub fn new(fd: AsyncFd) -> Self {
|
pub fn new(fd: AsyncFd) -> Self {
|
||||||
Self {
|
Self {
|
||||||
incoming: BufFdIn::new(fd),
|
incoming: BufFdIn::new(fd),
|
||||||
scratch: vec![]
|
scratch: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ impl IoIn {
|
||||||
pub struct IoOut {
|
pub struct IoOut {
|
||||||
outgoing: BufFdOut,
|
outgoing: BufFdOut,
|
||||||
scratch: Vec<u8>,
|
scratch: Vec<u8>,
|
||||||
fds: Vec<Rc<OwnedFd>>
|
fds: Vec<Rc<OwnedFd>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoOut {
|
impl IoOut {
|
||||||
|
|
@ -59,7 +59,7 @@ impl IoOut {
|
||||||
Self {
|
Self {
|
||||||
outgoing: BufFdOut::new(fd),
|
outgoing: BufFdOut::new(fd),
|
||||||
scratch: vec![],
|
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_registry::WlRegistry;
|
||||||
use crate::ifs::wl_seat::WlSeatGlobal;
|
use crate::ifs::wl_seat::WlSeatGlobal;
|
||||||
use crate::ifs::zwlr_layer_shell_v1::ZwlrLayerShellV1Global;
|
use crate::ifs::zwlr_layer_shell_v1::ZwlrLayerShellV1Global;
|
||||||
|
use crate::ifs::zxdg_output_manager_v1::ZxdgOutputManagerV1Global;
|
||||||
use crate::object::{Interface, ObjectId};
|
use crate::object::{Interface, ObjectId};
|
||||||
use crate::utils::copyhashmap::CopyHashMap;
|
use crate::utils::copyhashmap::CopyHashMap;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -19,7 +20,6 @@ use std::error::Error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use crate::ifs::zxdg_output_manager_v1::ZxdgOutputManagerV1Global;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum GlobalsError {
|
pub enum GlobalsError {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,6 @@ pub mod zwlr_layer_shell_v1;
|
||||||
pub mod zwp_linux_buffer_params_v1;
|
pub mod zwp_linux_buffer_params_v1;
|
||||||
pub mod zwp_linux_dmabuf_v1;
|
pub mod zwp_linux_dmabuf_v1;
|
||||||
pub mod zxdg_decoration_manager_v1;
|
pub mod zxdg_decoration_manager_v1;
|
||||||
pub mod zxdg_toplevel_decoration_v1;
|
|
||||||
pub mod zxdg_output_manager_v1;
|
pub mod zxdg_output_manager_v1;
|
||||||
pub mod zxdg_output_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 crate::wire::WlCompositorId;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use crate::xwayland::XWaylandEvent;
|
||||||
|
|
||||||
pub struct WlCompositorGlobal {
|
pub struct WlCompositorGlobal {
|
||||||
name: GlobalName,
|
name: GlobalName,
|
||||||
|
|
@ -51,6 +52,9 @@ impl WlCompositor {
|
||||||
let surface = Rc::new(WlSurface::new(surface.id, &self.client));
|
let surface = Rc::new(WlSurface::new(surface.id, &self.client));
|
||||||
track!(self.client, surface);
|
track!(self.client, surface);
|
||||||
self.client.add_client_obj(&surface)?;
|
self.client.add_client_obj(&surface)?;
|
||||||
|
if let Some(queue) = &self.client.xwayland_queue {
|
||||||
|
queue.push(XWaylandEvent::SurfaceCreated(surface.clone()));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ use bstr::ByteSlice;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
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;
|
const PRIME: u32 = 1;
|
||||||
|
|
||||||
|
|
@ -52,7 +56,7 @@ impl Global for WlDrmGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
1
|
2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,6 +106,60 @@ impl WlDrm {
|
||||||
let _req: CreatePlanarBuffer = self.client.parse(&**self, parser)?;
|
let _req: CreatePlanarBuffer = self.client.parse(&**self, parser)?;
|
||||||
Err(CreatePlanarBufferError::Unsupported)
|
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! {
|
object_base! {
|
||||||
|
|
@ -110,11 +168,12 @@ object_base! {
|
||||||
AUTHENTICATE => authenticate,
|
AUTHENTICATE => authenticate,
|
||||||
CREATE_BUFFER => create_buffer,
|
CREATE_BUFFER => create_buffer,
|
||||||
CREATE_PLANAR_BUFFER => create_planar_buffer,
|
CREATE_PLANAR_BUFFER => create_planar_buffer,
|
||||||
|
CREATE_PRIME_BUFFER => create_prime_buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Object for WlDrm {
|
impl Object for WlDrm {
|
||||||
fn num_requests(&self) -> u32 {
|
fn num_requests(&self) -> u32 {
|
||||||
CREATE_PLANAR_BUFFER + 1
|
CREATE_PRIME_BUFFER + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,6 +187,8 @@ pub enum WlDrmError {
|
||||||
CreateBufferError(#[from] CreateBufferError),
|
CreateBufferError(#[from] CreateBufferError),
|
||||||
#[error("Could not process a `create_planar_buffer` request")]
|
#[error("Could not process a `create_planar_buffer` request")]
|
||||||
CreatePlanarBufferError(#[from] CreatePlanarBufferError),
|
CreatePlanarBufferError(#[from] CreatePlanarBufferError),
|
||||||
|
#[error("Could not process a `create_prime_buffer` request")]
|
||||||
|
CreatePrimeBufferError(#[from] CreatePrimeBufferError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ClientError(Box<ClientError>),
|
ClientError(Box<ClientError>),
|
||||||
}
|
}
|
||||||
|
|
@ -157,3 +218,19 @@ pub enum CreatePlanarBufferError {
|
||||||
Unsupported,
|
Unsupported,
|
||||||
}
|
}
|
||||||
efrom!(CreatePlanarBufferError, ParseError, MsgParserError);
|
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::backend::Output;
|
||||||
use crate::client::{Client, ClientError, ClientId};
|
use crate::client::{Client, ClientError, ClientId};
|
||||||
use crate::globals::{Global, GlobalName};
|
use crate::globals::{Global, GlobalName};
|
||||||
|
use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
|
||||||
use crate::leaks::Tracker;
|
use crate::leaks::Tracker;
|
||||||
use crate::object::Object;
|
use crate::object::Object;
|
||||||
|
use crate::rect::Rect;
|
||||||
|
use crate::tree::OutputNode;
|
||||||
use crate::utils::buffd::MsgParser;
|
use crate::utils::buffd::MsgParser;
|
||||||
use crate::utils::buffd::MsgParserError;
|
use crate::utils::buffd::MsgParserError;
|
||||||
|
use crate::utils::copyhashmap::CopyHashMap;
|
||||||
use crate::wire::wl_output::*;
|
use crate::wire::wl_output::*;
|
||||||
use crate::wire::{WlOutputId, ZxdgOutputV1Id};
|
use crate::wire::{WlOutputId, ZxdgOutputV1Id};
|
||||||
|
use crate::CloneCell;
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
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;
|
const SP_UNKNOWN: i32 = 0;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
@ -83,7 +83,8 @@ impl WlOutputGlobal {
|
||||||
let changed = old_width != width || old_height != height;
|
let changed = old_width != width || old_height != height;
|
||||||
|
|
||||||
if changed {
|
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();
|
let bindings = self.bindings.borrow_mut();
|
||||||
for binding in bindings.values() {
|
for binding in bindings.values() {
|
||||||
for binding in binding.values() {
|
for binding in binding.values() {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
mod event_handling;
|
mod event_handling;
|
||||||
|
mod kb_owner;
|
||||||
mod pointer_owner;
|
mod pointer_owner;
|
||||||
pub mod wl_keyboard;
|
pub mod wl_keyboard;
|
||||||
pub mod wl_pointer;
|
pub mod wl_pointer;
|
||||||
pub mod wl_touch;
|
pub mod wl_touch;
|
||||||
mod kb_owner;
|
|
||||||
|
|
||||||
use crate::async_engine::SpawnedFuture;
|
use crate::async_engine::SpawnedFuture;
|
||||||
use crate::client::{Client, ClientError, ClientId};
|
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_device_v1::ZwpPrimarySelectionDeviceV1;
|
||||||
use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1;
|
use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1;
|
||||||
use crate::ifs::ipc::IpcError;
|
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::pointer_owner::PointerOwnerHolder;
|
||||||
use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE};
|
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_pointer::WlPointer;
|
||||||
use crate::ifs::wl_seat::wl_touch::WlTouch;
|
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::ifs::wl_surface::WlSurface;
|
||||||
use crate::leaks::Tracker;
|
use crate::leaks::Tracker;
|
||||||
use crate::object::{Object, ObjectId};
|
use crate::object::{Object, ObjectId};
|
||||||
|
|
@ -48,8 +49,7 @@ use std::ops::{Deref, DerefMut};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uapi::{c, Errno, OwnedFd};
|
use uapi::{c, Errno, OwnedFd};
|
||||||
use crate::ifs::wl_output::{WlOutputGlobal};
|
use crate::tree::toplevel::ToplevelNode;
|
||||||
use crate::ifs::wl_seat::kb_owner::KbOwnerHolder;
|
|
||||||
|
|
||||||
const POINTER: u32 = 1;
|
const POINTER: u32 = 1;
|
||||||
const KEYBOARD: u32 = 2;
|
const KEYBOARD: u32 = 2;
|
||||||
|
|
@ -95,7 +95,7 @@ pub struct WlSeatGlobal {
|
||||||
pos: Cell<(Fixed, Fixed)>,
|
pos: Cell<(Fixed, Fixed)>,
|
||||||
pointer_stack: RefCell<Vec<Rc<dyn Node>>>,
|
pointer_stack: RefCell<Vec<Rc<dyn Node>>>,
|
||||||
found_tree: RefCell<Vec<FoundNode>>,
|
found_tree: RefCell<Vec<FoundNode>>,
|
||||||
toplevel_focus_history: LinkedList<Rc<XdgToplevel>>,
|
toplevel_focus_history: LinkedList<Rc<dyn ToplevelNode>>,
|
||||||
keyboard_node: CloneCell<Rc<dyn Node>>,
|
keyboard_node: CloneCell<Rc<dyn Node>>,
|
||||||
pressed_keys: RefCell<AHashSet<u32>>,
|
pressed_keys: RefCell<AHashSet<u32>>,
|
||||||
bindings: RefCell<AHashMap<ClientId, AHashMap<WlSeatId, Rc<WlSeat>>>>,
|
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_pointer::{WlPointer, POINTER_FRAME_SINCE_VERSION};
|
||||||
use crate::ifs::wl_seat::{wl_keyboard, wl_pointer, Dnd, SeatId, WlSeat, WlSeatGlobal};
|
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_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::ifs::wl_surface::WlSurface;
|
||||||
use crate::object::ObjectId;
|
use crate::object::ObjectId;
|
||||||
use crate::tree::{FloatNode, Node};
|
use crate::tree::{FloatNode, Node};
|
||||||
|
|
@ -23,6 +21,7 @@ use i4config::keyboard::ModifiedKeySym;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use crate::tree::toplevel::ToplevelNode;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct NodeSeatState {
|
pub struct NodeSeatState {
|
||||||
|
|
@ -86,7 +85,7 @@ impl NodeSeatState {
|
||||||
seat.ungrab_kb();
|
seat.ungrab_kb();
|
||||||
seat.keyboard_node.set(seat.state.root.clone());
|
seat.keyboard_node.set(seat.state.root.clone());
|
||||||
if let Some(tl) = seat.toplevel_focus_history.last() {
|
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()
|
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();
|
let is_container = new.is_container();
|
||||||
for tl in self.toplevel_focus_history.rev_iter() {
|
for tl in self.toplevel_focus_history.rev_iter() {
|
||||||
if !tl.parent_is_float() && (!is_container || !tl.is_contained_in(new.id())) {
|
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()));
|
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());
|
let node = self.toplevel_focus_history.add_last(n.clone());
|
||||||
n.toplevel_history.insert(self.id(), node);
|
n.set_focus_history_link(self, node);
|
||||||
self.focus_xdg_surface(&n.xdg);
|
self.focus_node(n.focus_surface(self));
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_xdg_surface(self: &Rc<Self>, xdg: &Rc<XdgSurface>) {
|
|
||||||
self.focus_node(xdg.focus_surface(self));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ungrab_kb(self: &Rc<Self>) {
|
fn ungrab_kb(self: &Rc<Self>) {
|
||||||
|
|
@ -415,7 +410,7 @@ impl WlSeatGlobal {
|
||||||
|
|
||||||
// Enter callbacks
|
// Enter callbacks
|
||||||
impl WlSeatGlobal {
|
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);
|
self.focus_toplevel(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::rc::Rc;
|
|
||||||
use crate::CloneCell;
|
|
||||||
use crate::ifs::wl_seat::WlSeatGlobal;
|
use crate::ifs::wl_seat::WlSeatGlobal;
|
||||||
use crate::tree::Node;
|
use crate::tree::Node;
|
||||||
|
use crate::CloneCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct KbOwnerHolder {
|
pub struct KbOwnerHolder {
|
||||||
default: Rc<DefaultKbOwner>,
|
default: Rc<DefaultKbOwner>,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ pub mod cursor;
|
||||||
pub mod wl_subsurface;
|
pub mod wl_subsurface;
|
||||||
pub mod xdg_surface;
|
pub mod xdg_surface;
|
||||||
pub mod zwlr_layer_surface_v1;
|
pub mod zwlr_layer_surface_v1;
|
||||||
|
pub mod xwindow;
|
||||||
|
|
||||||
use crate::backend::{KeyState, ScrollAxis};
|
use crate::backend::{KeyState, ScrollAxis};
|
||||||
use crate::client::{Client, ClientError, RequestParser};
|
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::cursor::CursorSurface;
|
||||||
use crate::ifs::wl_surface::wl_subsurface::WlSubsurface;
|
use crate::ifs::wl_surface::wl_subsurface::WlSubsurface;
|
||||||
use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceRole};
|
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::leaks::Tracker;
|
||||||
use crate::object::Object;
|
use crate::object::Object;
|
||||||
use crate::pixman::Region;
|
use crate::pixman::Region;
|
||||||
|
|
@ -35,7 +37,6 @@ use std::mem;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const INVALID_SCALE: u32 = 0;
|
const INVALID_SCALE: u32 = 0;
|
||||||
|
|
@ -52,6 +53,7 @@ pub enum SurfaceRole {
|
||||||
Cursor,
|
Cursor,
|
||||||
DndIcon,
|
DndIcon,
|
||||||
ZwlrLayerSurface,
|
ZwlrLayerSurface,
|
||||||
|
XSurface,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceRole {
|
impl SurfaceRole {
|
||||||
|
|
@ -63,6 +65,7 @@ impl SurfaceRole {
|
||||||
SurfaceRole::Cursor => "cursor",
|
SurfaceRole::Cursor => "cursor",
|
||||||
SurfaceRole::DndIcon => "dnd_icon",
|
SurfaceRole::DndIcon => "dnd_icon",
|
||||||
SurfaceRole::ZwlrLayerSurface => "zwlr_layer_surface",
|
SurfaceRole::ZwlrLayerSurface => "zwlr_layer_surface",
|
||||||
|
SurfaceRole::XSurface => "xwayland surface",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,6 +126,14 @@ trait SurfaceExt {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_surface_destroy(&self) -> Result<(), WlSurfaceError> {
|
||||||
|
if self.is_some() {
|
||||||
|
Err(WlSurfaceError::ReloObjectStillExists)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn update_subsurface_parent_extents(&self) {
|
fn update_subsurface_parent_extents(&self) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
@ -343,10 +354,8 @@ impl WlSurface {
|
||||||
let _req: Destroy = self.parse(parser)?;
|
let _req: Destroy = self.parse(parser)?;
|
||||||
self.unset_dnd_icons();
|
self.unset_dnd_icons();
|
||||||
self.unset_cursors();
|
self.unset_cursors();
|
||||||
|
self.ext.get().on_surface_destroy()?;
|
||||||
self.destroy_node(true);
|
self.destroy_node(true);
|
||||||
if self.ext.get().is_some() {
|
|
||||||
return Err(DestroyError::ReloObjectStillExists);
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
let mut children = self.children.borrow_mut();
|
let mut children = self.children.borrow_mut();
|
||||||
if let Some(children) = &mut *children {
|
if let Some(children) = &mut *children {
|
||||||
|
|
@ -784,6 +793,8 @@ pub enum WlSurfaceError {
|
||||||
old: SurfaceRole,
|
old: SurfaceRole,
|
||||||
new: SurfaceRole,
|
new: SurfaceRole,
|
||||||
},
|
},
|
||||||
|
#[error("Cannot destroy a `wl_surface` before its role object")]
|
||||||
|
ReloObjectStillExists,
|
||||||
}
|
}
|
||||||
efrom!(WlSurfaceError, ClientError);
|
efrom!(WlSurfaceError, ClientError);
|
||||||
efrom!(WlSurfaceError, XdgSurfaceError);
|
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||||
|
|
@ -805,11 +816,12 @@ pub enum DestroyError {
|
||||||
ParseFailed(#[source] Box<MsgParserError>),
|
ParseFailed(#[source] Box<MsgParserError>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ClientError(Box<ClientError>),
|
ClientError(Box<ClientError>),
|
||||||
#[error("Cannot destroy a `wl_surface` before its role object")]
|
#[error(transparent)]
|
||||||
ReloObjectStillExists,
|
WlSurfaceError(Box<WlSurfaceError>),
|
||||||
}
|
}
|
||||||
efrom!(DestroyError, ParseFailed, MsgParserError);
|
efrom!(DestroyError, ParseFailed, MsgParserError);
|
||||||
efrom!(DestroyError, ClientError);
|
efrom!(DestroyError, ClientError);
|
||||||
|
efrom!(DestroyError, WlSurfaceError);
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum AttachError {
|
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) {
|
fn destroy_node(&self) {
|
||||||
self.workspace.set(None);
|
self.workspace.set(None);
|
||||||
self.surface.destroy_node(false);
|
self.surface.destroy_node(false);
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,10 @@ use std::fmt::{Debug, Formatter};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use backtrace::Backtrace;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use crate::ifs::wl_surface::WlSurface;
|
||||||
|
use crate::tree::toplevel::ToplevelNode;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, FromPrimitive)]
|
#[derive(Copy, Clone, Debug, FromPrimitive)]
|
||||||
pub enum ResizeEdge {
|
pub enum ResizeEdge {
|
||||||
|
|
@ -75,7 +77,7 @@ pub struct XdgToplevel {
|
||||||
pub parent: CloneCell<Option<Rc<XdgToplevel>>>,
|
pub parent: CloneCell<Option<Rc<XdgToplevel>>>,
|
||||||
pub children: RefCell<AHashMap<XdgToplevelId, Rc<XdgToplevel>>>,
|
pub children: RefCell<AHashMap<XdgToplevelId, Rc<XdgToplevel>>>,
|
||||||
states: RefCell<AHashSet<u32>>,
|
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>,
|
active_surfaces: NumCell<u32>,
|
||||||
pub decoration: Cell<Decoration>,
|
pub decoration: Cell<Decoration>,
|
||||||
bugs: Cell<&'static Bugs>,
|
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) {
|
fn send_configure_checked(&self, mut width: i32, mut height: i32) {
|
||||||
width = width.max(1);
|
width = width.max(1);
|
||||||
height = height.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) {
|
fn do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _direction: Direction) {
|
||||||
seat.focus_toplevel(&self);
|
seat.focus_toplevel(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn absolute_position(&self) -> Rect {
|
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) {
|
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>) {
|
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 {
|
impl XdgSurfaceExt for XdgToplevel {
|
||||||
fn focus_parent(&self, seat: &Rc<WlSeatGlobal>) {
|
fn focus_parent(&self, seat: &Rc<WlSeatGlobal>) {
|
||||||
self.parent_node.get().map(|p| p.focus_self(seat));
|
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();
|
let seats = surface.client.state.globals.lock_seats();
|
||||||
for seat in seats.values() {
|
for seat in seats.values() {
|
||||||
seat.focus_toplevel(&self);
|
seat.focus_toplevel(self.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
surface.client.state.tree_changed();
|
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 crate::client::{Client, ClientError};
|
||||||
use std::ops::Deref;
|
use crate::ifs::wl_seat::NodeSeatState;
|
||||||
use crate::client::{Client, ClientError, ClientId};
|
use crate::ifs::wl_surface::{
|
||||||
use crate::ifs::wl_output::{WlOutput, WlOutputGlobal};
|
CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
|
||||||
use crate::ifs::wl_surface::{CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError};
|
};
|
||||||
use crate::ifs::zwlr_layer_shell_v1::{OVERLAY, ZwlrLayerShellV1};
|
use crate::ifs::zwlr_layer_shell_v1::{ZwlrLayerShellV1, OVERLAY};
|
||||||
use crate::leaks::Tracker;
|
use crate::leaks::Tracker;
|
||||||
use crate::object::Object;
|
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::rect::Rect;
|
||||||
use crate::render::Renderer;
|
use crate::render::Renderer;
|
||||||
use crate::tree::{ContainerNode, ContainerSplit, FindTreeResult, FloatNode, FoundNode, Node, NodeId, OutputNode, WorkspaceNode};
|
|
||||||
use crate::tree::walker::NodeVisitor;
|
use crate::tree::walker::NodeVisitor;
|
||||||
|
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, OutputNode};
|
||||||
use crate::utils::bitflags::BitflagsExt;
|
use crate::utils::bitflags::BitflagsExt;
|
||||||
|
use crate::utils::buffd::MsgParser;
|
||||||
|
use crate::utils::buffd::MsgParserError;
|
||||||
use crate::utils::linkedlist::LinkedNode;
|
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;
|
const KI_NONE: u32 = 0;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
@ -100,7 +97,7 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
margin: Cell::new((0, 0, 0, 0)),
|
margin: Cell::new((0, 0, 0, 0)),
|
||||||
keyboard_interactivity: Cell::new(0),
|
keyboard_interactivity: Cell::new(0),
|
||||||
link: Cell::new(None),
|
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 {
|
if req.width > u16::MAX as u32 || req.height > u16::MAX as u32 {
|
||||||
return Err(SetSizeError::ExcessiveSize);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,16 +152,25 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
|
|
||||||
fn set_margin(&self, parser: MsgParser<'_, '_>) -> Result<(), SetMarginError> {
|
fn set_margin(&self, parser: MsgParser<'_, '_>) -> Result<(), SetMarginError> {
|
||||||
let req: SetMargin = self.client.parse(self, parser)?;
|
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(())
|
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)?;
|
let req: SetKeyboardInteractivity = self.client.parse(self, parser)?;
|
||||||
if req.keyboard_interactivity > KI_ON_DEMAND {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +279,8 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
} else if anchor.contains(BOTTOM) {
|
} else if anchor.contains(BOTTOM) {
|
||||||
y1 += opos.height() - height;
|
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();
|
self.client.state.tree_changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -308,7 +317,7 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
|
||||||
if was_active {
|
if was_active {
|
||||||
self.surface.active_changed(false);
|
self.surface.active_changed(false);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
KI_ON_DEMAND => self.surface.seat_state.release_kb_grab(),
|
KI_ON_DEMAND => self.surface.seat_state.release_kb_grab(),
|
||||||
KI_EXCLUSIVE => {
|
KI_EXCLUSIVE => {
|
||||||
let seats = self.client.state.globals.seats.lock();
|
let seats = self.client.state.globals.seats.lock();
|
||||||
|
|
@ -362,7 +371,7 @@ impl Node for ZwlrLayerSurfaceV1 {
|
||||||
tree.push(FoundNode {
|
tree.push(FoundNode {
|
||||||
node: self.surface.clone(),
|
node: self.surface.clone(),
|
||||||
x,
|
x,
|
||||||
y
|
y,
|
||||||
});
|
});
|
||||||
self.surface.find_tree_at(x, y, tree)
|
self.surface.find_tree_at(x, y, tree)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::client::{Client, ClientError};
|
use crate::client::{Client, ClientError};
|
||||||
use crate::globals::{Global, GlobalName};
|
use crate::globals::{Global, GlobalName};
|
||||||
|
use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
|
||||||
use crate::leaks::Tracker;
|
use crate::leaks::Tracker;
|
||||||
use crate::object::Object;
|
use crate::object::Object;
|
||||||
use crate::utils::buffd::MsgParser;
|
use crate::utils::buffd::MsgParser;
|
||||||
use crate::utils::buffd::MsgParserError;
|
use crate::utils::buffd::MsgParserError;
|
||||||
use crate::wire::zxdg_output_manager_v1::*;
|
use crate::wire::zxdg_output_manager_v1::*;
|
||||||
use crate::wire::{ZxdgOutputManagerV1Id};
|
use crate::wire::ZxdgOutputManagerV1Id;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
|
|
||||||
|
|
||||||
pub struct ZxdgOutputManagerV1Global {
|
pub struct ZxdgOutputManagerV1Global {
|
||||||
name: GlobalName,
|
name: GlobalName,
|
||||||
|
|
@ -51,10 +51,7 @@ impl ZxdgOutputManagerV1 {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_xdg_output(
|
fn get_xdg_output(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), GetXdgOutputError> {
|
||||||
self: &Rc<Self>,
|
|
||||||
parser: MsgParser<'_, '_>,
|
|
||||||
) -> Result<(), GetXdgOutputError> {
|
|
||||||
let req: GetXdgOutput = self.client.parse(&**self, parser)?;
|
let req: GetXdgOutput = self.client.parse(&**self, parser)?;
|
||||||
let output = self.client.lookup(req.output)?;
|
let output = self.client.lookup(req.output)?;
|
||||||
let xdg_output = Rc::new(ZxdgOutputV1 {
|
let xdg_output = Rc::new(ZxdgOutputV1 {
|
||||||
|
|
@ -62,7 +59,7 @@ impl ZxdgOutputManagerV1 {
|
||||||
version: self.version,
|
version: self.version,
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
output: output.clone(),
|
output: output.clone(),
|
||||||
tracker: Default::default()
|
tracker: Default::default(),
|
||||||
});
|
});
|
||||||
track!(self.client, xdg_output);
|
track!(self.client, xdg_output);
|
||||||
self.client.add_client_obj(&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::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::leaks::Tracker;
|
||||||
use crate::object::Object;
|
use crate::object::Object;
|
||||||
use crate::utils::buffd::{MsgParser, MsgParserError};
|
use crate::utils::buffd::{MsgParser, MsgParserError};
|
||||||
use crate::wire::ZxdgOutputV1Id;
|
|
||||||
use crate::wire::zxdg_output_v1::*;
|
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;
|
pub const NAME_SINCE: u32 = 2;
|
||||||
|
#[allow(dead_code)]
|
||||||
pub const DESCRIPTION_SINCE: u32 = 2;
|
pub const DESCRIPTION_SINCE: u32 = 2;
|
||||||
pub const NO_DONE_SINCE: u32 = 3;
|
pub const NO_DONE_SINCE: u32 = 3;
|
||||||
|
|
||||||
|
|
@ -38,11 +40,10 @@ impl ZxdgOutputV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_done(&self) {
|
pub fn send_done(&self) {
|
||||||
self.client.event(Done {
|
self.client.event(Done { self_id: self.id });
|
||||||
self_id: self.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn send_name(&self, name: &str) {
|
pub fn send_name(&self, name: &str) {
|
||||||
self.client.event(Name {
|
self.client.event(Name {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
@ -50,6 +51,7 @@ impl ZxdgOutputV1 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn send_description(&self, description: &str) {
|
pub fn send_description(&self, description: &str) {
|
||||||
self.client.event(Description {
|
self.client.event(Description {
|
||||||
self_id: self.id,
|
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(
|
#![allow(
|
||||||
clippy::len_zero,
|
clippy::len_zero,
|
||||||
clippy::needless_lifetimes,
|
clippy::needless_lifetimes,
|
||||||
|
|
@ -178,7 +178,9 @@ fn main_() -> Result<(), MainError> {
|
||||||
let _float_render_titles = engine.spawn2(Phase::PostLayout, float_titles(state.clone()));
|
let _float_render_titles = engine.spawn2(Phase::PostLayout, float_titles(state.clone()));
|
||||||
let socket_path = Acceptor::install(&state)?;
|
let socket_path = Acceptor::install(&state)?;
|
||||||
forker.setenv(b"WAYLAND_DISPLAY", socket_path.as_bytes());
|
forker.setenv(b"WAYLAND_DISPLAY", socket_path.as_bytes());
|
||||||
|
let _xwayland = engine.spawn(xwayland::manage(state.clone()));
|
||||||
el.run()?;
|
el.run()?;
|
||||||
|
drop(_xwayland);
|
||||||
state.clients.clear();
|
state.clients.clear();
|
||||||
for (_, seat) in state.globals.seats.lock().deref() {
|
for (_, seat) in state.globals.seats.lock().deref() {
|
||||||
seat.clear();
|
seat.clear();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::format::{Format, ARGB8888};
|
use crate::format::{Format, ARGB8888};
|
||||||
use crate::ifs::wl_buffer::WlBuffer;
|
use crate::ifs::wl_buffer::WlBuffer;
|
||||||
use crate::ifs::wl_surface::xdg_surface::XdgSurface;
|
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::ifs::wl_surface::WlSurface;
|
||||||
use crate::rect::Rect;
|
use crate::rect::Rect;
|
||||||
use crate::render::gl::frame_buffer::{with_scissor, GlFrameBuffer};
|
use crate::render::gl::frame_buffer::{with_scissor, GlFrameBuffer};
|
||||||
|
|
@ -21,7 +22,6 @@ use crate::State;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::slice;
|
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 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);
|
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();
|
let pos = ls.position();
|
||||||
self.render_layer_surface(ls.deref(), pos.x1(), pos.y1());
|
self.render_layer_surface(ls.deref(), pos.x1(), pos.y1());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
render_layer!(output.layers[0]);
|
render_layer!(output.layers[0]);
|
||||||
render_layer!(output.layers[1]);
|
render_layer!(output.layers[1]);
|
||||||
|
|
|
||||||
|
|
@ -117,9 +117,9 @@ impl State {
|
||||||
let seat = self.seat_queue.last();
|
let seat = self.seat_queue.last();
|
||||||
if let Some(seat) = seat {
|
if let Some(seat) = seat {
|
||||||
if let Some(prev) = seat.last_tiled_keyboard_toplevel(&*node) {
|
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() {
|
if let Some(container) = container.into_container() {
|
||||||
container.add_child_after(&*prev, node);
|
container.add_child_after(prev.as_node(), node);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,18 @@ use crate::NumCell;
|
||||||
pub use container::*;
|
pub use container::*;
|
||||||
pub use float::*;
|
pub use float::*;
|
||||||
use i4config::Direction;
|
use i4config::Direction;
|
||||||
|
pub use output::*;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
pub use workspace::*;
|
pub use workspace::*;
|
||||||
pub use output::*;
|
|
||||||
|
|
||||||
mod container;
|
mod container;
|
||||||
mod float;
|
mod float;
|
||||||
|
mod output;
|
||||||
pub mod walker;
|
pub mod walker;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
mod output;
|
pub mod toplevel;
|
||||||
|
|
||||||
pub struct NodeIds {
|
pub struct NodeIds {
|
||||||
next: NumCell<u32>,
|
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::cursor::KnownCursor;
|
||||||
use crate::ifs::wl_output::WlOutputGlobal;
|
use crate::ifs::wl_output::WlOutputGlobal;
|
||||||
use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal};
|
use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal};
|
||||||
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
||||||
use crate::rect::Rect;
|
use crate::rect::Rect;
|
||||||
use crate::render::Renderer;
|
use crate::render::Renderer;
|
||||||
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode};
|
|
||||||
use crate::tree::walker::NodeVisitor;
|
use crate::tree::walker::NodeVisitor;
|
||||||
|
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode};
|
||||||
use crate::utils::linkedlist::LinkedList;
|
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);
|
tree_id!(OutputNodeId);
|
||||||
pub struct OutputNode {
|
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_popup::XdgPopup;
|
||||||
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
|
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::ifs::wl_surface::WlSurface;
|
||||||
use crate::tree::{ContainerNode, FloatNode, Node, OutputNode, WorkspaceNode};
|
use crate::tree::{ContainerNode, FloatNode, Node, OutputNode, WorkspaceNode};
|
||||||
use crate::DisplayNode;
|
use crate::DisplayNode;
|
||||||
use std::rc::Rc;
|
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 {
|
pub trait NodeVisitorBase: Sized {
|
||||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
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>) {
|
fn visit_layer_surface(&mut self, node: &Rc<ZwlrLayerSurfaceV1>) {
|
||||||
node.visit_children(self);
|
node.visit_children(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_xwindow(&mut self, node: &Rc<Xwindow>) {
|
||||||
|
node.visit_children(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NodeVisitor {
|
pub trait NodeVisitor {
|
||||||
|
|
@ -54,6 +59,7 @@ pub trait NodeVisitor {
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>);
|
fn visit_float(&mut self, node: &Rc<FloatNode>);
|
||||||
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>);
|
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>);
|
||||||
fn visit_layer_surface(&mut self, node: &Rc<ZwlrLayerSurfaceV1>);
|
fn visit_layer_surface(&mut self, node: &Rc<ZwlrLayerSurfaceV1>);
|
||||||
|
fn visit_xwindow(&mut self, node: &Rc<Xwindow>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: NodeVisitorBase> NodeVisitor for T {
|
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>) {
|
fn visit_layer_surface(&mut self, node: &Rc<ZwlrLayerSurfaceV1>) {
|
||||||
<T as NodeVisitorBase>::visit_layer_surface(self, node)
|
<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 {
|
// pub fn visit_containers<F: FnMut(&Rc<ContainerNode>)>(f: F) -> impl NodeVisitor {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
pub trait BitflagsExt {
|
pub trait BitflagsExt {
|
||||||
fn contains(self, other: Self) -> bool;
|
fn contains(self, other: Self) -> bool;
|
||||||
|
fn intersects(self, other: Self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! num {
|
macro_rules! num {
|
||||||
($ty:ident) => {
|
($ty:ident) => {
|
||||||
impl BitflagsExt for $ty {
|
impl BitflagsExt for $ty {
|
||||||
fn contains(self, other: Self) -> bool {
|
fn contains(self, other: Self) -> bool {
|
||||||
|
self & other == other
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersects(self, other: Self) -> bool {
|
||||||
self & other != 0
|
self & other != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,11 @@ impl BufFdIn {
|
||||||
name: uapi::sockaddr_none_mut(),
|
name: uapi::sockaddr_none_mut(),
|
||||||
flags: 0,
|
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((iov, _, _)) if iov.is_empty() => return Err(BufFdError::Closed),
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(Errno(c::EAGAIN)) => return Ok(true),
|
Err(Errno(c::EAGAIN)) => return Ok(true),
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,11 @@ impl BufFdOut {
|
||||||
Ok(false)
|
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;
|
let mut read_pos = 0;
|
||||||
while read_pos < buf.len() {
|
while read_pos < buf.len() {
|
||||||
if self.flush_sync2(&mut read_pos, buf, fds)? {
|
if self.flush_sync2(&mut read_pos, buf, fds)? {
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,5 @@ pub mod ptr_ext;
|
||||||
pub mod queue;
|
pub mod queue;
|
||||||
pub mod smallmap;
|
pub mod smallmap;
|
||||||
pub mod stack;
|
pub mod stack;
|
||||||
|
pub mod tri;
|
||||||
pub mod vec_ext;
|
pub mod vec_ext;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,10 @@ impl<T> AsyncQueue<T> {
|
||||||
AsyncQueuePop { queue: self }
|
AsyncQueuePop { queue: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn non_empty<'a>(&'a self) -> AsyncQueueNonEmpty<'a, T> {
|
||||||
|
AsyncQueueNonEmpty { queue: self }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
mem::take(&mut *self.data.borrow_mut());
|
mem::take(&mut *self.data.borrow_mut());
|
||||||
self.waiter.take();
|
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