feat: split shell control connection
This commit is contained in:
1 parent
0c6fefacc1
commit
e511c0c31d
2 files changed
+70
-16
No files matched your search
@@ -34,8 +34,55 @@ pub(crate) fn prepare_command(
|
|||||||
state: &mut Ferese,
|
state: &mut Ferese,
|
||||||
command: &mut Command,
|
command: &mut Command,
|
||||||
capabilities: ClientCapabilities,
|
capabilities: ClientCapabilities,
|
||||||
|
) -> io::Result<Vec<UnixStream>> {
|
||||||
|
let split_shell_control = capabilities.contains(ClientCapabilities::EFFECTS)
|
||||||
|
&& capabilities.contains(ClientCapabilities::SHELL_CONTROL);
|
||||||
|
let primary_capabilities = if split_shell_control {
|
||||||
|
ClientCapabilities::EFFECTS
|
||||||
|
} else {
|
||||||
|
capabilities
|
||||||
|
};
|
||||||
|
let primary = insert_private_client(state, primary_capabilities)?;
|
||||||
|
let mut clients = vec![primary];
|
||||||
|
|
||||||
|
command.env_remove("WAYLAND_DISPLAY");
|
||||||
|
command.env_remove("FERESE_SHELL_CONTROL_SOCKET");
|
||||||
|
command.env("WAYLAND_SOCKET", clients[0].as_raw_fd().to_string());
|
||||||
|
|
||||||
|
if split_shell_control {
|
||||||
|
let control = insert_private_client(state, ClientCapabilities::SHELL_CONTROL)?;
|
||||||
|
|
||||||
|
command.env(
|
||||||
|
"FERESE_SHELL_CONTROL_SOCKET",
|
||||||
|
control.as_raw_fd().to_string(),
|
||||||
|
);
|
||||||
|
clients.push(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inherited_fds = clients.iter().map(AsRawFd::as_raw_fd).collect::<Vec<_>>();
|
||||||
|
// SAFETY: fcntl(F_SETFD) is async-signal-safe. The closure only clears
|
||||||
|
// CLOEXEC on sockets already owned by this Command's parent process.
|
||||||
|
unsafe {
|
||||||
|
command.pre_exec(move || {
|
||||||
|
for fd in &inherited_fds {
|
||||||
|
if libc::fcntl(*fd, libc::F_SETFD, 0) == -1 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(clients)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_private_client(
|
||||||
|
state: &mut Ferese,
|
||||||
|
capabilities: ClientCapabilities,
|
||||||
) -> io::Result<UnixStream> {
|
) -> io::Result<UnixStream> {
|
||||||
let (server, client) = UnixStream::pair()?;
|
let (server, client) = UnixStream::pair()?;
|
||||||
|
|
||||||
state.display_handle.insert_client(
|
state.display_handle.insert_client(
|
||||||
server,
|
server,
|
||||||
Arc::new(ClientState {
|
Arc::new(ClientState {
|
||||||
@@ -44,20 +91,6 @@ pub(crate) fn prepare_command(
|
|||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let client_fd = client.as_raw_fd();
|
|
||||||
command.env_remove("WAYLAND_DISPLAY");
|
|
||||||
command.env("WAYLAND_SOCKET", client_fd.to_string());
|
|
||||||
// SAFETY: fcntl(F_SETFD) is async-signal-safe. The closure only clears
|
|
||||||
// CLOEXEC on the socket already owned by this Command's parent process.
|
|
||||||
unsafe {
|
|
||||||
command.pre_exec(move || {
|
|
||||||
if libc::fcntl(client_fd, libc::F_SETFD, 0) == -1 {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
use std::error::Error;
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
os::{
|
||||||
|
fd::{FromRawFd, RawFd},
|
||||||
|
unix::net::UnixStream,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use ferese_protocols::shell::v1::client::{
|
use ferese_protocols::shell::v1::client::{
|
||||||
ferese_shell_manager_v1::FereseShellManagerV1,
|
ferese_shell_manager_v1::FereseShellManagerV1,
|
||||||
@@ -10,7 +16,7 @@ use wayland_client::{
|
|||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let expect_hidden = std::env::args_os().any(|argument| argument == "--expect-hidden");
|
let expect_hidden = std::env::args_os().any(|argument| argument == "--expect-hidden");
|
||||||
let connection = Connection::connect_to_env()?;
|
let connection = control_connection()?;
|
||||||
let mut queue = connection.new_event_queue();
|
let mut queue = connection.new_event_queue();
|
||||||
let qh = queue.handle();
|
let qh = queue.handle();
|
||||||
connection.display().get_registry(&qh, ());
|
connection.display().get_registry(&qh, ());
|
||||||
@@ -62,6 +68,21 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn control_connection() -> Result<Connection, Box<dyn Error>> {
|
||||||
|
let Some(raw_fd) = std::env::var_os("FERESE_SHELL_CONTROL_SOCKET") else {
|
||||||
|
return Ok(Connection::connect_to_env()?);
|
||||||
|
};
|
||||||
|
let fd = raw_fd
|
||||||
|
.to_str()
|
||||||
|
.ok_or("FERESE_SHELL_CONTROL_SOCKET is not valid UTF-8")?
|
||||||
|
.parse::<RawFd>()?;
|
||||||
|
// SAFETY: Ferese passes ownership of this inherited descriptor to the
|
||||||
|
// launched shell process. This function consumes it exactly once.
|
||||||
|
let socket = unsafe { UnixStream::from_raw_fd(fd) };
|
||||||
|
|
||||||
|
Ok(Connection::from_socket(socket)?)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ProbeState {
|
struct ProbeState {
|
||||||
manager: Option<FereseShellManagerV1>,
|
manager: Option<FereseShellManagerV1>,
|
||||||
|
|||||||
Reference in new issue
Block a user