1
0
Fork 0
forked from wry/wry

cli: add run-tagged subcommand

This commit is contained in:
Julian Orth 2026-02-27 21:59:30 +01:00
parent a1df575262
commit 3e7ca00565
7 changed files with 176 additions and 1 deletions

View file

@ -11,6 +11,7 @@ mod quit;
mod randr;
mod reexec;
mod run_privileged;
mod run_tagged;
pub mod screenshot;
mod seat_test;
mod set_log_level;
@ -24,7 +25,7 @@ use {
cli::{
clients::ClientsArgs, color_management::ColorManagementArgs,
damage_tracking::DamageTrackingArgs, idle::IdleCmd, input::InputArgs, randr::RandrArgs,
reexec::ReexecArgs, tree::TreeArgs, xwayland::XwaylandArgs,
reexec::ReexecArgs, run_tagged::RunTaggedArgs, tree::TreeArgs, xwayland::XwaylandArgs,
},
compositor::start_compositor,
format::{Format, ref_formats},
@ -72,6 +73,8 @@ pub enum Cmd {
Idle(IdleArgs),
/// Run a privileged program.
RunPrivileged(RunPrivilegedArgs),
/// Run a program with a connection tag.
RunTagged(RunTaggedArgs),
/// Tests the events produced by a seat.
SeatTest(SeatTestArgs),
/// Run the desktop portal.
@ -245,6 +248,7 @@ pub fn main() {
Cmd::Idle(a) => idle::main(cli.global, a),
Cmd::Unlock => unlock::main(cli.global),
Cmd::RunPrivileged(a) => run_privileged::main(cli.global, a),
Cmd::RunTagged(a) => run_tagged::main(cli.global, a),
Cmd::SeatTest(a) => seat_test::main(cli.global, a),
Cmd::Portal => portal::run_freestanding(cli.global),
Cmd::Randr(a) => randr::main(cli.global, a),

74
src/cli/run_tagged.rs Normal file
View file

@ -0,0 +1,74 @@
use {
crate::{
cli::GlobalArgs,
compositor::WAYLAND_DISPLAY,
tools::tool_client::{Handle, ToolClient, with_tool_client},
utils::{errorfmt::ErrorFmt, oserror::OsError},
wire::{jay_acceptor_request, jay_compositor},
},
clap::{Args, ValueHint},
std::{cell::Cell, env, rc::Rc},
uapi::UstrPtr,
};
#[derive(Args, Debug)]
pub struct RunTaggedArgs {
/// Specifies a tag to apply to all spawned wayland connections.
tag: String,
/// The program to run.
#[clap(required = true, trailing_var_arg = true, value_hint = ValueHint::CommandWithArguments)]
pub program: Vec<String>,
}
pub fn main(global: GlobalArgs, run_tagged_args: RunTaggedArgs) {
with_tool_client(global.log_level.into(), |tc| async move {
let run_tagged = Rc::new(RunTagged { tc: tc.clone() });
run_tagged.run(run_tagged_args).await;
});
}
struct RunTagged {
tc: Rc<ToolClient>,
}
impl RunTagged {
async fn run(&self, args: RunTaggedArgs) {
let tc = &self.tc;
let comp = tc.jay_compositor().await;
let req = tc.id();
tc.send(jay_compositor::GetTaggedAcceptor {
self_id: comp,
id: req,
tag: &args.tag,
});
let res = Rc::new(Cell::new(None));
jay_acceptor_request::Done::handle(&tc, req, res.clone(), |res, ev| {
res.set(Some(Ok(ev.name.to_owned())));
});
jay_acceptor_request::Failed::handle(&tc, req, res.clone(), |res, ev| {
res.set(Some(Err(ev.msg.to_owned())));
});
tc.round_trip().await;
match res.take().unwrap() {
Ok(n) => {
unsafe {
env::set_var(WAYLAND_DISPLAY, &n);
}
let mut argv = UstrPtr::new();
for arg in &args.program {
argv.push(arg.as_str());
}
let program = args.program[0].as_str();
let res = uapi::execvp(program, &argv).unwrap_err();
fatal!(
"Could not execute `{}`: {}",
program,
ErrorFmt(OsError::from(res)),
);
}
Err(msg) => {
fatal!("Could not create acceptor: {}", msg);
}
}
}
}

View file

@ -11,6 +11,7 @@ pub mod ext_session_lock_manager_v1;
pub mod ext_session_lock_v1;
pub mod head_management;
pub mod ipc;
pub mod jay_acceptor_request;
pub mod jay_client_query;
pub mod jay_color_management;
pub mod jay_compositor;

View file

@ -0,0 +1,60 @@
use {
crate::{
client::{Client, ClientError},
leaks::Tracker,
object::{Object, Version},
utils::errorfmt::ErrorFmt,
wire::{JayAcceptorRequestId, jay_acceptor_request::*},
},
std::{error::Error, rc::Rc},
thiserror::Error,
};
pub struct JayAcceptorRequest {
pub id: JayAcceptorRequestId,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl JayAcceptorRequest {
pub fn send_done(&self, name: &str) {
self.client.event(Done {
self_id: self.id,
name,
});
}
pub fn send_failed(&self, err: impl Error) {
let msg = &ErrorFmt(err).to_string();
self.client.event(Failed {
self_id: self.id,
msg,
});
}
}
impl JayAcceptorRequestRequestHandler for JayAcceptorRequest {
type Error = JayAcceptorRequestError;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = JayAcceptorRequest;
version = self.version;
}
impl Object for JayAcceptorRequest {}
simple_add_obj!(JayAcceptorRequest);
#[derive(Debug, Error)]
pub enum JayAcceptorRequestError {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(JayAcceptorRequestError, ClientError);

View file

@ -4,6 +4,7 @@ use {
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError, ClientId},
globals::{Global, GlobalName},
ifs::{
jay_acceptor_request::JayAcceptorRequest,
jay_client_query::JayClientQuery,
jay_color_management::JayColorManagement,
jay_ei_session_builder::JayEiSessionBuilder,
@ -515,6 +516,27 @@ impl JayCompositorRequestHandler for JayCompositor {
self.client.add_client_obj(&obj)?;
Ok(())
}
fn get_tagged_acceptor(
&self,
req: GetTaggedAcceptor<'_>,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let obj = Rc::new(JayAcceptorRequest {
id: req.id,
client: self.client.clone(),
tracker: Default::default(),
version: self.version,
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
let state = &self.client.state;
match state.tagged_acceptors.get(state, req.tag) {
Ok(d) => obj.send_done(&d),
Err(e) => obj.send_failed(e),
}
Ok(())
}
}
object_base! {

View file

@ -0,0 +1,9 @@
request destroy { }
event done {
name: str,
}
event failed {
msg: str,
}

View file

@ -121,6 +121,11 @@ request create_tree_query (since = 18) {
id: id(jay_tree_query),
}
request get_tagged_acceptor (since = 25) {
id: id(jay_acceptor_request),
tag: str,
}
# events
event client_id {