Merge pull request #763 from mahkoh/jorth/client-tag
compositor: add tagged acceptors
This commit is contained in:
commit
d87a5827b5
40 changed files with 585 additions and 46 deletions
|
|
@ -204,6 +204,7 @@ The full specification of client criteria can be found in
|
||||||
- `is-xwayland` - Matches if the client is/isn't Xwayland.
|
- `is-xwayland` - Matches if the client is/isn't Xwayland.
|
||||||
- `comm`, `comm-regex` - Matches the `/proc/self/comm` of the client.
|
- `comm`, `comm-regex` - Matches the `/proc/self/comm` of the client.
|
||||||
- `exe`, `exe-regex` - Matches the `/proc/self/exe` of the client.
|
- `exe`, `exe-regex` - Matches the `/proc/self/exe` of the client.
|
||||||
|
- `tag`, `tag-regex` - Matches the tag of the client.
|
||||||
|
|
||||||
## Window Rules
|
## Window Rules
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ pub enum ClientCriterionStringField {
|
||||||
SandboxInstanceId,
|
SandboxInstanceId,
|
||||||
Comm,
|
Comm,
|
||||||
Exe,
|
Exe,
|
||||||
|
Tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,15 @@ impl ConfigClient {
|
||||||
.drain()
|
.drain()
|
||||||
.map(|(a, b)| (a, b.into_raw_fd()))
|
.map(|(a, b)| (a, b.into_raw_fd()))
|
||||||
.collect();
|
.collect();
|
||||||
if fds.is_empty() {
|
if command.tag.is_some() {
|
||||||
|
self.send(&ClientMessage::Run3 {
|
||||||
|
prog: &command.prog,
|
||||||
|
args: command.args.clone(),
|
||||||
|
env,
|
||||||
|
fds,
|
||||||
|
tag: command.tag.as_deref(),
|
||||||
|
});
|
||||||
|
} else if fds.is_empty() {
|
||||||
self.send(&ClientMessage::Run {
|
self.send(&ClientMessage::Run {
|
||||||
prog: &command.prog,
|
prog: &command.prog,
|
||||||
args: command.args.clone(),
|
args: command.args.clone(),
|
||||||
|
|
@ -1764,6 +1772,8 @@ impl ConfigClient {
|
||||||
ClientCriterion::CommRegex(t) => string!(t, Comm, true),
|
ClientCriterion::CommRegex(t) => string!(t, Comm, true),
|
||||||
ClientCriterion::Exe(t) => string!(t, Exe, false),
|
ClientCriterion::Exe(t) => string!(t, Exe, false),
|
||||||
ClientCriterion::ExeRegex(t) => string!(t, Exe, true),
|
ClientCriterion::ExeRegex(t) => string!(t, Exe, true),
|
||||||
|
ClientCriterion::Tag(t) => string!(t, Tag, false),
|
||||||
|
ClientCriterion::TagRegex(t) => string!(t, Tag, true),
|
||||||
};
|
};
|
||||||
let res = self.send_with_response(&ClientMessage::CreateClientMatcher { criterion });
|
let res = self.send_with_response(&ClientMessage::CreateClientMatcher { criterion });
|
||||||
get_response!(
|
get_response!(
|
||||||
|
|
|
||||||
|
|
@ -834,6 +834,13 @@ pub enum ClientMessage<'a> {
|
||||||
SetXWaylandEnabled {
|
SetXWaylandEnabled {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
},
|
},
|
||||||
|
Run3 {
|
||||||
|
prog: &'a str,
|
||||||
|
args: Vec<String>,
|
||||||
|
env: Vec<(String, String)>,
|
||||||
|
fds: Vec<(i32, i32)>,
|
||||||
|
tag: Option<&'a str>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,10 @@ pub enum ClientCriterion<'a> {
|
||||||
Exe(&'a str),
|
Exe(&'a str),
|
||||||
/// Matches the `/proc/pid/exe` of the client with a regular expression.
|
/// Matches the `/proc/pid/exe` of the client with a regular expression.
|
||||||
ExeRegex(&'a str),
|
ExeRegex(&'a str),
|
||||||
|
/// Matches the tag of the client verbatim.
|
||||||
|
Tag(&'a str),
|
||||||
|
/// Matches the tag of the client with a regular expression.
|
||||||
|
TagRegex(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientCriterion<'_> {
|
impl ClientCriterion<'_> {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ pub struct Command {
|
||||||
pub(crate) args: Vec<String>,
|
pub(crate) args: Vec<String>,
|
||||||
pub(crate) env: HashMap<String, String>,
|
pub(crate) env: HashMap<String, String>,
|
||||||
pub(crate) fds: RefCell<HashMap<i32, OwnedFd>>,
|
pub(crate) fds: RefCell<HashMap<i32, OwnedFd>>,
|
||||||
|
pub(crate) tag: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
|
|
@ -37,6 +38,7 @@ impl Command {
|
||||||
args: vec![],
|
args: vec![],
|
||||||
env: Default::default(),
|
env: Default::default(),
|
||||||
fds: Default::default(),
|
fds: Default::default(),
|
||||||
|
tag: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,6 +99,12 @@ impl Command {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a tag to Wayland connections created by the spawned command.
|
||||||
|
pub fn tag(&mut self, tag: &str) -> &mut Self {
|
||||||
|
self.tag = Some(tag.to_owned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Executes the command.
|
/// Executes the command.
|
||||||
///
|
///
|
||||||
/// This consumes all attached file descriptors.
|
/// This consumes all attached file descriptors.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ mod quit;
|
||||||
mod randr;
|
mod randr;
|
||||||
mod reexec;
|
mod reexec;
|
||||||
mod run_privileged;
|
mod run_privileged;
|
||||||
|
mod run_tagged;
|
||||||
pub mod screenshot;
|
pub mod screenshot;
|
||||||
mod seat_test;
|
mod seat_test;
|
||||||
mod set_log_level;
|
mod set_log_level;
|
||||||
|
|
@ -24,7 +25,7 @@ use {
|
||||||
cli::{
|
cli::{
|
||||||
clients::ClientsArgs, color_management::ColorManagementArgs,
|
clients::ClientsArgs, color_management::ColorManagementArgs,
|
||||||
damage_tracking::DamageTrackingArgs, idle::IdleCmd, input::InputArgs, randr::RandrArgs,
|
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,
|
compositor::start_compositor,
|
||||||
format::{Format, ref_formats},
|
format::{Format, ref_formats},
|
||||||
|
|
@ -72,6 +73,8 @@ pub enum Cmd {
|
||||||
Idle(IdleArgs),
|
Idle(IdleArgs),
|
||||||
/// Run a privileged program.
|
/// Run a privileged program.
|
||||||
RunPrivileged(RunPrivilegedArgs),
|
RunPrivileged(RunPrivilegedArgs),
|
||||||
|
/// Run a program with a connection tag.
|
||||||
|
RunTagged(RunTaggedArgs),
|
||||||
/// Tests the events produced by a seat.
|
/// Tests the events produced by a seat.
|
||||||
SeatTest(SeatTestArgs),
|
SeatTest(SeatTestArgs),
|
||||||
/// Run the desktop portal.
|
/// Run the desktop portal.
|
||||||
|
|
@ -245,6 +248,7 @@ pub fn main() {
|
||||||
Cmd::Idle(a) => idle::main(cli.global, a),
|
Cmd::Idle(a) => idle::main(cli.global, a),
|
||||||
Cmd::Unlock => unlock::main(cli.global),
|
Cmd::Unlock => unlock::main(cli.global),
|
||||||
Cmd::RunPrivileged(a) => run_privileged::main(cli.global, a),
|
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::SeatTest(a) => seat_test::main(cli.global, a),
|
||||||
Cmd::Portal => portal::run_freestanding(cli.global),
|
Cmd::Portal => portal::run_freestanding(cli.global),
|
||||||
Cmd::Randr(a) => randr::main(cli.global, a),
|
Cmd::Randr(a) => randr::main(cli.global, a),
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ pub struct Client {
|
||||||
pub is_xwayland: bool,
|
pub is_xwayland: bool,
|
||||||
pub comm: Option<String>,
|
pub comm: Option<String>,
|
||||||
pub exe: Option<String>,
|
pub exe: Option<String>,
|
||||||
|
pub tag: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_client_query(
|
pub async fn handle_client_query(
|
||||||
|
|
@ -201,6 +202,9 @@ pub async fn handle_client_query(
|
||||||
Exe::handle(tl, id, c.clone(), |c, event| {
|
Exe::handle(tl, id, c.clone(), |c, event| {
|
||||||
last!(c).exe = Some(event.exe.to_string());
|
last!(c).exe = Some(event.exe.to_string());
|
||||||
});
|
});
|
||||||
|
Tag::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
last!(c).tag = Some(event.tag.to_string());
|
||||||
|
});
|
||||||
tl.round_trip().await;
|
tl.round_trip().await;
|
||||||
mem::take(&mut *c.borrow_mut())
|
mem::take(&mut *c.borrow_mut())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -239,5 +243,6 @@ impl ClientPrinter<'_> {
|
||||||
bol!(is_xwayland, "xwayland");
|
bol!(is_xwayland, "xwayland");
|
||||||
opt!(comm, "comm");
|
opt!(comm, "comm");
|
||||||
opt!(exe, "exe");
|
opt!(exe, "exe");
|
||||||
|
opt!(tag, "tag");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -427,10 +427,9 @@ impl Input {
|
||||||
async fn handle_keymap(&self, input: JayInputId) -> Vec<u8> {
|
async fn handle_keymap(&self, input: JayInputId) -> Vec<u8> {
|
||||||
let data = Rc::new(RefCell::new(Vec::new()));
|
let data = Rc::new(RefCell::new(Vec::new()));
|
||||||
jay_input::Keymap::handle(&self.tc, input, data.clone(), |d, map| {
|
jay_input::Keymap::handle(&self.tc, input, data.clone(), |d, map| {
|
||||||
let mem = Rc::new(
|
let len = map.keymap_len as _;
|
||||||
ClientMem::new_private(&map.keymap, map.keymap_len as _, true, None, None).unwrap(),
|
let mem = Rc::new(ClientMem::new_private(&map.keymap, len, true, None, None).unwrap())
|
||||||
)
|
.offset(0, len);
|
||||||
.offset(0);
|
|
||||||
mem.read(d.borrow_mut().deref_mut()).unwrap();
|
mem.read(d.borrow_mut().deref_mut()).unwrap();
|
||||||
});
|
});
|
||||||
self.tc.round_trip().await;
|
self.tc.round_trip().await;
|
||||||
|
|
|
||||||
74
src/cli/run_tagged.rs
Normal file
74
src/cli/run_tagged.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -131,12 +131,12 @@ impl ClientMem {
|
||||||
self.data.len()
|
self.data.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(self: &Rc<Self>, offset: usize) -> ClientMemOffset {
|
pub fn offset(self: &Rc<Self>, offset: usize, len: usize) -> ClientMemOffset {
|
||||||
let mem = unsafe { &*self.data };
|
let mem = unsafe { &*self.data };
|
||||||
ClientMemOffset {
|
ClientMemOffset {
|
||||||
mem: self.clone(),
|
mem: self.clone(),
|
||||||
offset,
|
offset,
|
||||||
data: &mem[offset..],
|
data: &mem[offset..][..len],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,7 @@ fn start_compositor2(
|
||||||
keyboard_state_ids: Default::default(),
|
keyboard_state_ids: Default::default(),
|
||||||
physical_keyboard_ids: Default::default(),
|
physical_keyboard_ids: Default::default(),
|
||||||
security_context_acceptors: Default::default(),
|
security_context_acceptors: Default::default(),
|
||||||
|
tagged_acceptors: Default::default(),
|
||||||
cursor_user_group_ids: Default::default(),
|
cursor_user_group_ids: Default::default(),
|
||||||
cursor_user_ids: Default::default(),
|
cursor_user_ids: Default::default(),
|
||||||
cursor_user_groups: Default::default(),
|
cursor_user_groups: Default::default(),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use {
|
||||||
},
|
},
|
||||||
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientId},
|
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientId},
|
||||||
cmm::cmm_eotf::Eotf,
|
cmm::cmm_eotf::Eotf,
|
||||||
compositor::MAX_EXTENTS,
|
compositor::{MAX_EXTENTS, WAYLAND_DISPLAY},
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
criteria::{
|
criteria::{
|
||||||
CritLiteralOrRegex, CritMgrExt, CritTarget, CritUpstreamNode,
|
CritLiteralOrRegex, CritMgrExt, CritTarget, CritUpstreamNode,
|
||||||
|
|
@ -26,6 +26,7 @@ use {
|
||||||
output_schedule::map_cursor_hz,
|
output_schedule::map_cursor_hz,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
|
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
|
||||||
|
tagged_acceptor::TaggedAcceptorError,
|
||||||
theme::{Color, ThemeSized},
|
theme::{Color, ThemeSized},
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode,
|
ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode,
|
||||||
|
|
@ -1823,9 +1824,18 @@ impl ConfigProxyHandler {
|
||||||
&self,
|
&self,
|
||||||
prog: &str,
|
prog: &str,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
env: Vec<(String, String)>,
|
mut env: Vec<(String, String)>,
|
||||||
fds: Vec<(i32, i32)>,
|
fds: Vec<(i32, i32)>,
|
||||||
|
tag: Option<&str>,
|
||||||
) -> Result<(), CphError> {
|
) -> Result<(), CphError> {
|
||||||
|
if let Some(tag) = tag {
|
||||||
|
let display = self
|
||||||
|
.state
|
||||||
|
.tagged_acceptors
|
||||||
|
.get(&self.state, tag)
|
||||||
|
.map_err(CphError::CreateTaggedAcceptor)?;
|
||||||
|
env.push((WAYLAND_DISPLAY.to_string(), display.to_string()));
|
||||||
|
}
|
||||||
let fds: Vec<_> = fds
|
let fds: Vec<_> = fds
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(a, b)| (a, Rc::new(OwnedFd::new(b))))
|
.map(|(a, b)| (a, Rc::new(OwnedFd::new(b))))
|
||||||
|
|
@ -2107,6 +2117,7 @@ impl ConfigProxyHandler {
|
||||||
}
|
}
|
||||||
ClientCriterionStringField::Comm => mgr.comm(needle),
|
ClientCriterionStringField::Comm => mgr.comm(needle),
|
||||||
ClientCriterionStringField::Exe => mgr.exe(needle),
|
ClientCriterionStringField::Exe => mgr.exe(needle),
|
||||||
|
ClientCriterionStringField::Tag => mgr.tag(needle),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientCriterionIpc::Sandboxed => mgr.sandboxed(),
|
ClientCriterionIpc::Sandboxed => mgr.sandboxed(),
|
||||||
|
|
@ -2815,7 +2826,7 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::GetSeats => self.handle_get_seats(),
|
ClientMessage::GetSeats => self.handle_get_seats(),
|
||||||
ClientMessage::RemoveSeat { .. } => {}
|
ClientMessage::RemoveSeat { .. } => {}
|
||||||
ClientMessage::Run { prog, args, env } => {
|
ClientMessage::Run { prog, args, env } => {
|
||||||
self.handle_run(prog, args, env, vec![]).wrn("run")?
|
self.handle_run(prog, args, env, vec![], None).wrn("run")?
|
||||||
}
|
}
|
||||||
ClientMessage::GrabKb { kb, grab } => self.handle_grab(kb, grab).wrn("grab")?,
|
ClientMessage::GrabKb { kb, grab } => self.handle_grab(kb, grab).wrn("grab")?,
|
||||||
ClientMessage::SetColor { colorable, color } => {
|
ClientMessage::SetColor { colorable, color } => {
|
||||||
|
|
@ -3023,7 +3034,7 @@ impl ConfigProxyHandler {
|
||||||
args,
|
args,
|
||||||
env,
|
env,
|
||||||
fds,
|
fds,
|
||||||
} => self.handle_run(prog, args, env, fds).wrn("run")?,
|
} => self.handle_run(prog, args, env, fds, None).wrn("run")?,
|
||||||
ClientMessage::DisableDefaultSeat => self.state.create_default_seat.set(false),
|
ClientMessage::DisableDefaultSeat => self.state.create_default_seat.set(false),
|
||||||
ClientMessage::DestroyKeymap { keymap } => self.handle_destroy_keymap(keymap),
|
ClientMessage::DestroyKeymap { keymap } => self.handle_destroy_keymap(keymap),
|
||||||
ClientMessage::GetConnectorName { connector } => self
|
ClientMessage::GetConnectorName { connector } => self
|
||||||
|
|
@ -3393,6 +3404,13 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetXWaylandEnabled { enabled } => self
|
ClientMessage::SetXWaylandEnabled { enabled } => self
|
||||||
.handle_set_x_wayland_enabled(enabled)
|
.handle_set_x_wayland_enabled(enabled)
|
||||||
.wrn("set_x_wayland_enabled")?,
|
.wrn("set_x_wayland_enabled")?,
|
||||||
|
ClientMessage::Run3 {
|
||||||
|
prog,
|
||||||
|
args,
|
||||||
|
env,
|
||||||
|
fds,
|
||||||
|
tag,
|
||||||
|
} => self.handle_run(prog, args, env, fds, tag).wrn("run")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -3548,6 +3566,8 @@ enum CphError {
|
||||||
UnknownFallbackOutputMode(FallbackOutputMode),
|
UnknownFallbackOutputMode(FallbackOutputMode),
|
||||||
#[error("Unknown tile state {0:?}")]
|
#[error("Unknown tile state {0:?}")]
|
||||||
UnknownTileState(ConfigTileState),
|
UnknownTileState(ConfigTileState),
|
||||||
|
#[error("Could not create a tagged acceptor")]
|
||||||
|
CreateTaggedAcceptor(#[source] TaggedAcceptorError),
|
||||||
}
|
}
|
||||||
|
|
||||||
trait WithRequestName {
|
trait WithRequestName {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use {
|
||||||
clmm_sandboxed::ClmMatchSandboxed,
|
clmm_sandboxed::ClmMatchSandboxed,
|
||||||
clmm_string::{
|
clmm_string::{
|
||||||
ClmMatchComm, ClmMatchExe, ClmMatchSandboxAppId, ClmMatchSandboxEngine,
|
ClmMatchComm, ClmMatchExe, ClmMatchSandboxAppId, ClmMatchSandboxEngine,
|
||||||
ClmMatchSandboxInstanceId,
|
ClmMatchSandboxInstanceId, ClmMatchTag,
|
||||||
},
|
},
|
||||||
clmm_uid::ClmMatchUid,
|
clmm_uid::ClmMatchUid,
|
||||||
},
|
},
|
||||||
|
|
@ -61,6 +61,7 @@ pub struct RootMatchers {
|
||||||
pid: ClmRootMatcherMap<ClmMatchPid>,
|
pid: ClmRootMatcherMap<ClmMatchPid>,
|
||||||
comm: ClmRootMatcherMap<ClmMatchComm>,
|
comm: ClmRootMatcherMap<ClmMatchComm>,
|
||||||
exe: ClmRootMatcherMap<ClmMatchExe>,
|
exe: ClmRootMatcherMap<ClmMatchExe>,
|
||||||
|
tag: ClmRootMatcherMap<ClmMatchTag>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RootMatchers {
|
impl RootMatchers {
|
||||||
|
|
@ -72,6 +73,7 @@ impl RootMatchers {
|
||||||
self.pid.clear();
|
self.pid.clear();
|
||||||
self.comm.clear();
|
self.comm.clear();
|
||||||
self.exe.clear();
|
self.exe.clear();
|
||||||
|
self.tag.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,6 +183,7 @@ impl ClMatcherManager {
|
||||||
unconditional!(pid);
|
unconditional!(pid);
|
||||||
unconditional!(comm);
|
unconditional!(comm);
|
||||||
unconditional!(exe);
|
unconditional!(exe);
|
||||||
|
unconditional!(tag);
|
||||||
fixed!(sandboxed);
|
fixed!(sandboxed);
|
||||||
fixed!(is_xwayland);
|
fixed!(is_xwayland);
|
||||||
self.constant[true].handle(data);
|
self.constant[true].handle(data);
|
||||||
|
|
@ -222,6 +225,10 @@ impl ClMatcherManager {
|
||||||
pub fn exe(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
|
pub fn exe(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
|
||||||
self.root(ClmMatchExe::new(string))
|
self.root(ClmMatchExe::new(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tag(&self, string: CritLiteralOrRegex) -> Rc<ClmUpstreamNode> {
|
||||||
|
self.root(ClmMatchTag::new(string))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CritTarget for Rc<Client> {
|
impl CritTarget for Rc<Client> {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ pub type ClmMatchString<T> = CritMatchString<Rc<Client>, T>;
|
||||||
pub type ClmMatchSandboxEngine = ClmMatchString<AcceptorMetadataAccess<SandboxEngineField>>;
|
pub type ClmMatchSandboxEngine = ClmMatchString<AcceptorMetadataAccess<SandboxEngineField>>;
|
||||||
pub type ClmMatchSandboxAppId = ClmMatchString<AcceptorMetadataAccess<SandboxAppIdField>>;
|
pub type ClmMatchSandboxAppId = ClmMatchString<AcceptorMetadataAccess<SandboxAppIdField>>;
|
||||||
pub type ClmMatchSandboxInstanceId = ClmMatchString<AcceptorMetadataAccess<SandboxInstanceIdField>>;
|
pub type ClmMatchSandboxInstanceId = ClmMatchString<AcceptorMetadataAccess<SandboxInstanceIdField>>;
|
||||||
|
pub type ClmMatchTag = ClmMatchString<AcceptorMetadataAccess<TagField>>;
|
||||||
pub type ClmMatchComm = ClmMatchString<CommAccess>;
|
pub type ClmMatchComm = ClmMatchString<CommAccess>;
|
||||||
pub type ClmMatchExe = ClmMatchString<ExeAccess>;
|
pub type ClmMatchExe = ClmMatchString<ExeAccess>;
|
||||||
|
|
||||||
|
|
@ -22,7 +23,7 @@ pub struct AcceptorMetadataAccess<T>(PhantomData<T>);
|
||||||
pub struct CommAccess;
|
pub struct CommAccess;
|
||||||
pub struct ExeAccess;
|
pub struct ExeAccess;
|
||||||
|
|
||||||
trait SandboxField: Sized + 'static {
|
trait AcceptorMetadataField: Sized + 'static {
|
||||||
fn field(meta: &AcceptorMetadata) -> &Option<String>;
|
fn field(meta: &AcceptorMetadata) -> &Option<String>;
|
||||||
fn nodes(
|
fn nodes(
|
||||||
roots: &RootMatchers,
|
roots: &RootMatchers,
|
||||||
|
|
@ -32,10 +33,11 @@ trait SandboxField: Sized + 'static {
|
||||||
pub struct SandboxEngineField;
|
pub struct SandboxEngineField;
|
||||||
pub struct SandboxAppIdField;
|
pub struct SandboxAppIdField;
|
||||||
pub struct SandboxInstanceIdField;
|
pub struct SandboxInstanceIdField;
|
||||||
|
pub struct TagField;
|
||||||
|
|
||||||
impl<T> StringAccess<Rc<Client>> for AcceptorMetadataAccess<T>
|
impl<T> StringAccess<Rc<Client>> for AcceptorMetadataAccess<T>
|
||||||
where
|
where
|
||||||
T: SandboxField,
|
T: AcceptorMetadataField,
|
||||||
{
|
{
|
||||||
fn with_string(data: &Rc<Client>, f: impl FnOnce(&str) -> bool) -> bool {
|
fn with_string(data: &Rc<Client>, f: impl FnOnce(&str) -> bool) -> bool {
|
||||||
f(T::field(&data.acceptor).as_deref().unwrap_or_default())
|
f(T::field(&data.acceptor).as_deref().unwrap_or_default())
|
||||||
|
|
@ -46,7 +48,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SandboxField for SandboxEngineField {
|
impl AcceptorMetadataField for SandboxEngineField {
|
||||||
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
||||||
&meta.sandbox_engine
|
&meta.sandbox_engine
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +60,7 @@ impl SandboxField for SandboxEngineField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SandboxField for SandboxAppIdField {
|
impl AcceptorMetadataField for SandboxAppIdField {
|
||||||
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
||||||
&meta.app_id
|
&meta.app_id
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +72,7 @@ impl SandboxField for SandboxAppIdField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SandboxField for SandboxInstanceIdField {
|
impl AcceptorMetadataField for SandboxInstanceIdField {
|
||||||
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
||||||
&meta.instance_id
|
&meta.instance_id
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +84,18 @@ impl SandboxField for SandboxInstanceIdField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AcceptorMetadataField for TagField {
|
||||||
|
fn field(meta: &AcceptorMetadata) -> &Option<String> {
|
||||||
|
&meta.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nodes(
|
||||||
|
roots: &RootMatchers,
|
||||||
|
) -> &ClmRootMatcherMap<ClmMatchString<AcceptorMetadataAccess<Self>>> {
|
||||||
|
&roots.tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl StringAccess<Rc<Client>> for CommAccess {
|
impl StringAccess<Rc<Client>> for CommAccess {
|
||||||
fn with_string(data: &Rc<Client>, f: impl FnOnce(&str) -> bool) -> bool {
|
fn with_string(data: &Rc<Client>, f: impl FnOnce(&str) -> bool) -> bool {
|
||||||
f(&data.pid_info.comm)
|
f(&data.pid_info.comm)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ pub mod ext_session_lock_manager_v1;
|
||||||
pub mod ext_session_lock_v1;
|
pub mod ext_session_lock_v1;
|
||||||
pub mod head_management;
|
pub mod head_management;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
|
pub mod jay_acceptor_request;
|
||||||
pub mod jay_client_query;
|
pub mod jay_client_query;
|
||||||
pub mod jay_color_management;
|
pub mod jay_color_management;
|
||||||
pub mod jay_compositor;
|
pub mod jay_compositor;
|
||||||
|
|
|
||||||
60
src/ifs/jay_acceptor_request.rs
Normal file
60
src/ifs/jay_acceptor_request.rs
Normal 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);
|
||||||
|
|
@ -9,7 +9,7 @@ use {
|
||||||
jay_client_query::{
|
jay_client_query::{
|
||||||
AddAll, AddId, Comm, Destroy, Done, End, Exe, Execute, IsXwayland,
|
AddAll, AddId, Comm, Destroy, Done, End, Exe, Execute, IsXwayland,
|
||||||
JayClientQueryRequestHandler, Pid, SandboxAppId, SandboxEngine, SandboxInstanceId,
|
JayClientQueryRequestHandler, Pid, SandboxAppId, SandboxEngine, SandboxInstanceId,
|
||||||
Sandboxed, Start, Uid,
|
Sandboxed, Start, Tag, Uid,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -26,6 +26,8 @@ pub struct JayClientQuery {
|
||||||
all: Cell<bool>,
|
all: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TAG_SINCE: Version = Version(25);
|
||||||
|
|
||||||
impl JayClientQuery {
|
impl JayClientQuery {
|
||||||
pub fn new(client: &Rc<Client>, id: JayClientQueryId, version: Version) -> Self {
|
pub fn new(client: &Rc<Client>, id: JayClientQueryId, version: Version) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -95,6 +97,14 @@ impl JayClientQueryRequestHandler for JayClientQuery {
|
||||||
instance_id,
|
instance_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if self.version >= TAG_SINCE
|
||||||
|
&& let Some(tag) = &client.acceptor.tag
|
||||||
|
{
|
||||||
|
self.client.event(Tag {
|
||||||
|
self_id: self.id,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
|
}
|
||||||
self.client.event(End { self_id: self.id });
|
self.client.event(End { self_id: self.id });
|
||||||
};
|
};
|
||||||
if self.all.get() {
|
if self.all.get() {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use {
|
||||||
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError, ClientId},
|
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError, ClientId},
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
ifs::{
|
ifs::{
|
||||||
|
jay_acceptor_request::JayAcceptorRequest,
|
||||||
jay_client_query::JayClientQuery,
|
jay_client_query::JayClientQuery,
|
||||||
jay_color_management::JayColorManagement,
|
jay_color_management::JayColorManagement,
|
||||||
jay_ei_session_builder::JayEiSessionBuilder,
|
jay_ei_session_builder::JayEiSessionBuilder,
|
||||||
|
|
@ -79,7 +80,7 @@ impl Global for JayCompositorGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
24
|
25
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_caps(&self) -> ClientCaps {
|
fn required_caps(&self) -> ClientCaps {
|
||||||
|
|
@ -515,6 +516,27 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
Ok(())
|
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! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -197,14 +197,15 @@ impl JayInput {
|
||||||
where
|
where
|
||||||
F: FnOnce(&Rc<KbvmMap>) -> Result<(), JayInputError>,
|
F: FnOnce(&Rc<KbvmMap>) -> Result<(), JayInputError>,
|
||||||
{
|
{
|
||||||
|
let len = len as _;
|
||||||
let cm = Rc::new(ClientMem::new_private(
|
let cm = Rc::new(ClientMem::new_private(
|
||||||
keymap,
|
keymap,
|
||||||
len as _,
|
len,
|
||||||
true,
|
true,
|
||||||
Some(&self.client),
|
Some(&self.client),
|
||||||
None,
|
None,
|
||||||
)?)
|
)?)
|
||||||
.offset(0);
|
.offset(0, len);
|
||||||
let mut map = vec![];
|
let mut map = vec![];
|
||||||
cm.read(&mut map)?;
|
cm.read(&mut map)?;
|
||||||
self.or_error(|| {
|
self.or_error(|| {
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,8 @@ impl WlBuffer {
|
||||||
if required > mem.len() as u64 {
|
if required > mem.len() as u64 {
|
||||||
return Err(WlBufferError::OutOfBounds);
|
return Err(WlBufferError::OutOfBounds);
|
||||||
}
|
}
|
||||||
let mem = Rc::new(mem.offset(offset));
|
let size = bytes as usize;
|
||||||
|
let mem = Rc::new(mem.offset(offset, size));
|
||||||
let min_row_size = width as u64 * format.bpp as u64;
|
let min_row_size = width as u64 * format.bpp as u64;
|
||||||
if (stride as u64) < min_row_size {
|
if (stride as u64) < min_row_size {
|
||||||
return Err(WlBufferError::StrideTooSmall);
|
return Err(WlBufferError::StrideTooSmall);
|
||||||
|
|
@ -155,7 +156,7 @@ impl WlBuffer {
|
||||||
let udmabuf_impossible = !mem.pool().is_sealed_memfd();
|
let udmabuf_impossible = !mem.pool().is_sealed_memfd();
|
||||||
let dmabuf_buffer_params = match udmabuf {
|
let dmabuf_buffer_params = match udmabuf {
|
||||||
None => DmabufBufferParams {
|
None => DmabufBufferParams {
|
||||||
size: bytes as usize,
|
size,
|
||||||
udmabuf: None,
|
udmabuf: None,
|
||||||
udmabuf_offset: 0,
|
udmabuf_offset: 0,
|
||||||
udmabuf_size: 0,
|
udmabuf_size: 0,
|
||||||
|
|
|
||||||
|
|
@ -58,18 +58,13 @@ impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 {
|
||||||
if req.size > MAX_SIZE {
|
if req.size > MAX_SIZE {
|
||||||
return Err(ZwpVirtualKeyboardV1Error::OversizedKeymap);
|
return Err(ZwpVirtualKeyboardV1Error::OversizedKeymap);
|
||||||
}
|
}
|
||||||
let client_mem = ClientMem::new_private(
|
let size = req.size as usize - 1;
|
||||||
&req.fd,
|
let client_mem = ClientMem::new_private(&req.fd, size, true, Some(&self.client), None)
|
||||||
req.size as usize - 1,
|
.map(Rc::new)
|
||||||
true,
|
.map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?;
|
||||||
Some(&self.client),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.map(Rc::new)
|
|
||||||
.map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?;
|
|
||||||
let mut map = vec![];
|
let mut map = vec![];
|
||||||
client_mem
|
client_mem
|
||||||
.offset(0)
|
.offset(0, size)
|
||||||
.read(&mut map)
|
.read(&mut map)
|
||||||
.map_err(ZwpVirtualKeyboardV1Error::ReadKeymap)?;
|
.map_err(ZwpVirtualKeyboardV1Error::ReadKeymap)?;
|
||||||
let map = self
|
let map = self
|
||||||
|
|
|
||||||
|
|
@ -111,22 +111,21 @@ impl ZwlrGammaControlV1RequestHandler for ZwlrGammaControlV1 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3 color channels
|
// 3 color channels of u16
|
||||||
let data_size = gamma_lut_size * 3;
|
let data_size = size_of::<u16>() * (3 * gamma_lut_size) as usize;
|
||||||
|
|
||||||
let mut gamma_lut = vec![];
|
let mut gamma_lut = vec![];
|
||||||
Rc::new(ClientMem::new_private(
|
Rc::new(ClientMem::new_private(
|
||||||
&req.fd,
|
&req.fd,
|
||||||
(2 * data_size) as _,
|
data_size,
|
||||||
true,
|
true,
|
||||||
Some(&self.client),
|
Some(&self.client),
|
||||||
None,
|
None,
|
||||||
)?)
|
)?)
|
||||||
.offset(0)
|
.offset(0, data_size)
|
||||||
.read(&mut gamma_lut)?;
|
.read(&mut gamma_lut)?;
|
||||||
let gamma_lut = &gamma_lut[..data_size as _];
|
|
||||||
|
|
||||||
let gamma_lut = wayland_gamma_lut_to_drm_gamma_lut(gamma_lut);
|
let gamma_lut = wayland_gamma_lut_to_drm_gamma_lut(&gamma_lut);
|
||||||
let gamma_lut = Rc::new(BackendGammaLut::new(gamma_lut));
|
let gamma_lut = Rc::new(BackendGammaLut::new(gamma_lut));
|
||||||
if node.set_gamma_lut(Some(gamma_lut)).is_err() {
|
if node.set_gamma_lut(Some(gamma_lut)).is_err() {
|
||||||
fail();
|
fail();
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,9 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_keymap(fd: &Rc<OwnedFd>, size: usize) -> String {
|
fn read_keymap(fd: &Rc<OwnedFd>, size: usize) -> String {
|
||||||
let client_mem = ClientMem::new_private(fd, size - 1, true, None, None).unwrap();
|
let size = size - 1;
|
||||||
let client_mem = Rc::new(client_mem).offset(0);
|
let client_mem = ClientMem::new_private(fd, size, true, None, None).unwrap();
|
||||||
|
let client_mem = Rc::new(client_mem).offset(0, size);
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
client_mem.read(&mut v).unwrap();
|
client_mem.read(&mut v).unwrap();
|
||||||
v.as_bstr().to_string()
|
v.as_bstr().to_string()
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ mod screenshoter;
|
||||||
mod security_context_acceptor;
|
mod security_context_acceptor;
|
||||||
mod sighand;
|
mod sighand;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod tagged_acceptor;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
mod text;
|
mod text;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ pub struct AcceptorMetadata {
|
||||||
pub sandbox_engine: Option<String>,
|
pub sandbox_engine: Option<String>,
|
||||||
pub app_id: Option<String>,
|
pub app_id: Option<String>,
|
||||||
pub instance_id: Option<String>,
|
pub instance_id: Option<String>,
|
||||||
|
pub tag: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecurityContextAcceptors {
|
impl SecurityContextAcceptors {
|
||||||
|
|
@ -67,6 +68,7 @@ impl SecurityContextAcceptors {
|
||||||
sandbox_engine,
|
sandbox_engine,
|
||||||
app_id,
|
app_id,
|
||||||
instance_id,
|
instance_id,
|
||||||
|
tag: None,
|
||||||
}),
|
}),
|
||||||
listen_fd: listen_fd.clone(),
|
listen_fd: listen_fd.clone(),
|
||||||
close_fd: close_fd.clone(),
|
close_fd: close_fd.clone(),
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
security_context_acceptor::SecurityContextAcceptors,
|
security_context_acceptor::SecurityContextAcceptors,
|
||||||
|
tagged_acceptor::TaggedAcceptors,
|
||||||
theme::{Color, Theme},
|
theme::{Color, Theme},
|
||||||
time::Time,
|
time::Time,
|
||||||
tree::{
|
tree::{
|
||||||
|
|
@ -239,6 +240,7 @@ pub struct State {
|
||||||
pub keyboard_state_ids: KeyboardStateIds,
|
pub keyboard_state_ids: KeyboardStateIds,
|
||||||
pub physical_keyboard_ids: PhysicalKeyboardIds,
|
pub physical_keyboard_ids: PhysicalKeyboardIds,
|
||||||
pub security_context_acceptors: SecurityContextAcceptors,
|
pub security_context_acceptors: SecurityContextAcceptors,
|
||||||
|
pub tagged_acceptors: TaggedAcceptors,
|
||||||
pub cursor_user_group_ids: CursorUserGroupIds,
|
pub cursor_user_group_ids: CursorUserGroupIds,
|
||||||
pub cursor_user_ids: CursorUserIds,
|
pub cursor_user_ids: CursorUserIds,
|
||||||
pub cursor_user_groups: CopyHashMap<CursorUserGroupId, Rc<CursorUserGroup>>,
|
pub cursor_user_groups: CopyHashMap<CursorUserGroupId, Rc<CursorUserGroup>>,
|
||||||
|
|
@ -1086,6 +1088,7 @@ impl State {
|
||||||
self.workspace_watchers.clear();
|
self.workspace_watchers.clear();
|
||||||
self.toplevel_lists.clear();
|
self.toplevel_lists.clear();
|
||||||
self.security_context_acceptors.clear();
|
self.security_context_acceptors.clear();
|
||||||
|
self.tagged_acceptors.clear();
|
||||||
self.slow_clients.clear();
|
self.slow_clients.clear();
|
||||||
for h in self.input_device_handlers.borrow_mut().drain_values() {
|
for h in self.input_device_handlers.borrow_mut().drain_values() {
|
||||||
h.async_event.clear();
|
h.async_event.clear();
|
||||||
|
|
|
||||||
200
src/tagged_acceptor.rs
Normal file
200
src/tagged_acceptor.rs
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
async_engine::SpawnedFuture,
|
||||||
|
client::ClientCaps,
|
||||||
|
security_context_acceptor::AcceptorMetadata,
|
||||||
|
state::State,
|
||||||
|
utils::{errorfmt::ErrorFmt, numcell::NumCell, oserror::OsError, xrd::xrd},
|
||||||
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{Errno, OwnedFd, Ustring, c, format_ustr},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum TaggedAcceptorError {
|
||||||
|
#[error("XDG_RUNTIME_DIR is not set")]
|
||||||
|
XrdNotSet,
|
||||||
|
#[error("XDG_RUNTIME_DIR ({0:?}) is too long to form a unix socket address")]
|
||||||
|
XrdTooLong(String),
|
||||||
|
#[error("Could not create a wayland socket")]
|
||||||
|
SocketFailed(#[source] OsError),
|
||||||
|
#[error("Could not stat the existing socket")]
|
||||||
|
SocketStat(#[source] OsError),
|
||||||
|
#[error("Could not start listening for incoming connections")]
|
||||||
|
ListenFailed(#[source] OsError),
|
||||||
|
#[error("Could not open the lock file")]
|
||||||
|
OpenLockFile(#[source] OsError),
|
||||||
|
#[error("Could not lock the lock file")]
|
||||||
|
LockLockFile(#[source] OsError),
|
||||||
|
#[error("Could not bind the socket to an address")]
|
||||||
|
BindFailed(#[source] OsError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TaggedAcceptors {
|
||||||
|
acceptors: RefCell<AHashMap<String, Rc<Acceptor>>>,
|
||||||
|
next_name: NumCell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Acceptor {
|
||||||
|
socket: AllocatedSocket,
|
||||||
|
tag: String,
|
||||||
|
state: Rc<State>,
|
||||||
|
metadata: Rc<AcceptorMetadata>,
|
||||||
|
future: Cell<Option<SpawnedFuture<()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaggedAcceptors {
|
||||||
|
pub fn clear(&self) {
|
||||||
|
let acceptors = self.acceptors.take();
|
||||||
|
for (_, acceptor) in acceptors {
|
||||||
|
acceptor.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, state: &Rc<State>, tag: &str) -> Result<Rc<String>, TaggedAcceptorError> {
|
||||||
|
let acceptors = &mut *self.acceptors.borrow_mut();
|
||||||
|
if let Some(acceptor) = acceptors.get(tag) {
|
||||||
|
return Ok(acceptor.socket.name.clone());
|
||||||
|
}
|
||||||
|
let acceptor = Rc::new(Acceptor {
|
||||||
|
socket: self.allocate_socket()?,
|
||||||
|
tag: tag.to_owned(),
|
||||||
|
state: state.clone(),
|
||||||
|
metadata: Rc::new(AcceptorMetadata {
|
||||||
|
secure: false,
|
||||||
|
sandboxed: false,
|
||||||
|
sandbox_engine: Default::default(),
|
||||||
|
app_id: Default::default(),
|
||||||
|
instance_id: Default::default(),
|
||||||
|
tag: Some(tag.to_owned()),
|
||||||
|
}),
|
||||||
|
future: Default::default(),
|
||||||
|
});
|
||||||
|
log::info!("Creating tagged acceptor `{tag}`");
|
||||||
|
acceptor.future.set(Some(
|
||||||
|
state.eng.spawn("tagged accept", acceptor.clone().accept()),
|
||||||
|
));
|
||||||
|
acceptors.insert(tag.to_owned(), acceptor.clone());
|
||||||
|
Ok(acceptor.socket.name.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocate_socket(&self) -> Result<AllocatedSocket, TaggedAcceptorError> {
|
||||||
|
let xrd = xrd().ok_or(TaggedAcceptorError::XrdNotSet)?;
|
||||||
|
let socket = uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0)
|
||||||
|
.map(Rc::new)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(TaggedAcceptorError::SocketFailed)?;
|
||||||
|
loop {
|
||||||
|
let i = self.next_name.fetch_add(1) + 1000;
|
||||||
|
if let Some(s) = bind_socket(&socket, &xrd, i)? {
|
||||||
|
return Ok(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Acceptor {
|
||||||
|
fn kill(&self) {
|
||||||
|
log::info!("Destroying tagged acceptor `{}`", self.tag);
|
||||||
|
self.future.take();
|
||||||
|
self.state
|
||||||
|
.tagged_acceptors
|
||||||
|
.acceptors
|
||||||
|
.borrow_mut()
|
||||||
|
.remove(&self.tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn accept(self: Rc<Self>) {
|
||||||
|
let s = &self.state;
|
||||||
|
loop {
|
||||||
|
let fd = match s.ring.accept(&self.socket.socket, c::SOCK_CLOEXEC).await {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not accept a client: {}", ErrorFmt(e));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let id = s.clients.id();
|
||||||
|
if let Err(e) = s
|
||||||
|
.clients
|
||||||
|
.spawn(id, s, fd, ClientCaps::all(), false, &self.metadata)
|
||||||
|
{
|
||||||
|
log::error!("Could not spawn a client: {}", ErrorFmt(e));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AllocatedSocket {
|
||||||
|
// wayland-x
|
||||||
|
name: Rc<String>,
|
||||||
|
// /run/user/1000/wayland-x
|
||||||
|
path: Ustring,
|
||||||
|
socket: Rc<OwnedFd>,
|
||||||
|
// /run/user/1000/wayland-x.lock
|
||||||
|
lock_path: Ustring,
|
||||||
|
_lock_fd: OwnedFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AllocatedSocket {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = uapi::unlink(&self.path);
|
||||||
|
let _ = uapi::unlink(&self.lock_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_socket(
|
||||||
|
fd: &Rc<OwnedFd>,
|
||||||
|
xrd: &str,
|
||||||
|
id: u64,
|
||||||
|
) -> Result<Option<AllocatedSocket>, TaggedAcceptorError> {
|
||||||
|
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
|
||||||
|
addr.sun_family = c::AF_UNIX as _;
|
||||||
|
let name = Rc::new(format!("wayland-{}", id));
|
||||||
|
let path = format_ustr!("{}/{}", xrd, name);
|
||||||
|
let lock_path = format_ustr!("{}.lock", path.display());
|
||||||
|
if path.len() + 1 > addr.sun_path.len() {
|
||||||
|
return Err(TaggedAcceptorError::XrdTooLong(xrd.to_string()));
|
||||||
|
}
|
||||||
|
let lock_fd = uapi::open(&*lock_path, c::O_CREAT | c::O_CLOEXEC | c::O_RDWR, 0o644)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(TaggedAcceptorError::OpenLockFile)?;
|
||||||
|
if let Err(e) = uapi::flock(lock_fd.raw(), c::LOCK_EX | c::LOCK_NB) {
|
||||||
|
if e.0 == c::EWOULDBLOCK {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
return Err(TaggedAcceptorError::LockLockFile(e.into()));
|
||||||
|
}
|
||||||
|
match uapi::lstat(&path) {
|
||||||
|
Ok(_) => {
|
||||||
|
log::info!("Unlinking {}", path.display());
|
||||||
|
let _ = uapi::unlink(&path);
|
||||||
|
}
|
||||||
|
Err(Errno(c::ENOENT)) => {}
|
||||||
|
Err(e) => return Err(TaggedAcceptorError::SocketStat(e.into())),
|
||||||
|
}
|
||||||
|
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
|
||||||
|
sun_path[..path.len()].copy_from_slice(path.as_bytes());
|
||||||
|
sun_path[path.len()] = 0;
|
||||||
|
uapi::bind(fd.raw(), &addr)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(TaggedAcceptorError::BindFailed)?;
|
||||||
|
if let Err(e) = uapi::listen(fd.raw(), 4096) {
|
||||||
|
return Err(TaggedAcceptorError::ListenFailed(e.into()));
|
||||||
|
}
|
||||||
|
Ok(Some(AllocatedSocket {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
socket: fd.clone(),
|
||||||
|
lock_path,
|
||||||
|
_lock_fd: lock_fd,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
@ -335,7 +335,7 @@ impl ToolClient {
|
||||||
self_id: s.registry,
|
self_id: s.registry,
|
||||||
name: s.jay_compositor.0,
|
name: s.jay_compositor.0,
|
||||||
interface: JayCompositor.name(),
|
interface: JayCompositor.name(),
|
||||||
version: s.jay_compositor.1.min(24),
|
version: s.jay_compositor.1.min(25),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
});
|
});
|
||||||
self.jay_compositor.set(Some(id));
|
self.jay_compositor.set(Some(id));
|
||||||
|
|
|
||||||
|
|
@ -278,6 +278,8 @@ pub struct ClientMatch {
|
||||||
pub comm_regex: Option<String>,
|
pub comm_regex: Option<String>,
|
||||||
pub exe: Option<String>,
|
pub exe: Option<String>,
|
||||||
pub exe_regex: Option<String>,
|
pub exe_regex: Option<String>,
|
||||||
|
pub tag: Option<String>,
|
||||||
|
pub tag_regex: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -419,6 +421,7 @@ pub struct Exec {
|
||||||
pub args: Vec<String>,
|
pub args: Vec<String>,
|
||||||
pub envs: Vec<(String, String)>,
|
pub envs: Vec<(String, String)>,
|
||||||
pub privileged: bool,
|
pub privileged: bool,
|
||||||
|
pub tag: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,14 @@ impl Parser for ClientMatchParser<'_> {
|
||||||
sandbox_instance_id_regex,
|
sandbox_instance_id_regex,
|
||||||
uid,
|
uid,
|
||||||
pid,
|
pid,
|
||||||
is_xwayland,
|
|
||||||
comm,
|
comm,
|
||||||
comm_regex,
|
comm_regex,
|
||||||
exe,
|
exe,
|
||||||
exe_regex,
|
exe_regex,
|
||||||
|
tag,
|
||||||
|
tag_regex,
|
||||||
),
|
),
|
||||||
|
(is_xwayland,),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
opt(str("name")),
|
opt(str("name")),
|
||||||
|
|
@ -78,12 +80,14 @@ impl Parser for ClientMatchParser<'_> {
|
||||||
opt(str("sandbox-instance-id-regex")),
|
opt(str("sandbox-instance-id-regex")),
|
||||||
opt(s32("uid")),
|
opt(s32("uid")),
|
||||||
opt(s32("pid")),
|
opt(s32("pid")),
|
||||||
opt(bol("is-xwayland")),
|
|
||||||
opt(str("comm")),
|
opt(str("comm")),
|
||||||
opt(str("comm-regex")),
|
opt(str("comm-regex")),
|
||||||
opt(str("exe")),
|
opt(str("exe")),
|
||||||
opt(str("exe-regex")),
|
opt(str("exe-regex")),
|
||||||
|
opt(str("tag")),
|
||||||
|
opt(str("tag-regex")),
|
||||||
),
|
),
|
||||||
|
(opt(bol("is-xwayland")),),
|
||||||
))?;
|
))?;
|
||||||
let mut not = None;
|
let mut not = None;
|
||||||
if let Some(value) = not_val {
|
if let Some(value) = not_val {
|
||||||
|
|
@ -130,6 +134,8 @@ impl Parser for ClientMatchParser<'_> {
|
||||||
comm_regex: comm_regex.despan_into(),
|
comm_regex: comm_regex.despan_into(),
|
||||||
exe: exe.despan_into(),
|
exe: exe.despan_into(),
|
||||||
exe_regex: exe_regex.despan_into(),
|
exe_regex: exe_regex.despan_into(),
|
||||||
|
tag: tag.despan_into(),
|
||||||
|
tag_regex: tag_regex.despan_into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ impl Parser for ExecParser<'_> {
|
||||||
args: vec![],
|
args: vec![],
|
||||||
envs: vec![],
|
envs: vec![],
|
||||||
privileged: false,
|
privileged: false,
|
||||||
|
tag: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,6 +71,7 @@ impl Parser for ExecParser<'_> {
|
||||||
args,
|
args,
|
||||||
envs: vec![],
|
envs: vec![],
|
||||||
privileged: false,
|
privileged: false,
|
||||||
|
tag: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,12 +81,13 @@ impl Parser for ExecParser<'_> {
|
||||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||||
) -> ParseResult<Self> {
|
) -> ParseResult<Self> {
|
||||||
let mut ext = Extractor::new(self.0, span, table);
|
let mut ext = Extractor::new(self.0, span, table);
|
||||||
let (prog_opt, shell_opt, args_val, envs_val, privileged) = ext.extract((
|
let (prog_opt, shell_opt, args_val, envs_val, privileged, tag) = ext.extract((
|
||||||
opt(str("prog")),
|
opt(str("prog")),
|
||||||
opt(str("shell")),
|
opt(str("shell")),
|
||||||
opt(arr("args")),
|
opt(arr("args")),
|
||||||
opt(val("env")),
|
opt(val("env")),
|
||||||
recover(opt(bol("privileged"))),
|
recover(opt(bol("privileged"))),
|
||||||
|
opt(str("tag")),
|
||||||
))?;
|
))?;
|
||||||
let prog;
|
let prog;
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
|
|
@ -112,11 +115,21 @@ impl Parser for ExecParser<'_> {
|
||||||
None => vec![],
|
None => vec![],
|
||||||
Some(e) => e.parse_map(&mut EnvParser)?,
|
Some(e) => e.parse_map(&mut EnvParser)?,
|
||||||
};
|
};
|
||||||
|
if let Some(privileged) = privileged
|
||||||
|
&& privileged.value
|
||||||
|
&& tag.is_some()
|
||||||
|
{
|
||||||
|
log::warn!(
|
||||||
|
"Exec is privileged and tagged but tagged execs are always unprivileged: {}",
|
||||||
|
self.0.error3(privileged.span),
|
||||||
|
);
|
||||||
|
}
|
||||||
Ok(Exec {
|
Ok(Exec {
|
||||||
prog,
|
prog,
|
||||||
args,
|
args,
|
||||||
envs,
|
envs,
|
||||||
privileged: privileged.despan().unwrap_or(false),
|
privileged: privileged.despan().unwrap_or(false),
|
||||||
|
tag: tag.despan_into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1646,6 +1646,9 @@ fn create_command(exec: &Exec) -> Command {
|
||||||
if exec.privileged {
|
if exec.privileged {
|
||||||
command.privileged();
|
command.privileged();
|
||||||
}
|
}
|
||||||
|
if let Some(tag) = &exec.tag {
|
||||||
|
command.tag(tag);
|
||||||
|
}
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,8 @@ impl Rule for ClientRule {
|
||||||
value_ref!(CommRegex, comm_regex);
|
value_ref!(CommRegex, comm_regex);
|
||||||
value_ref!(Exe, exe);
|
value_ref!(Exe, exe);
|
||||||
value_ref!(ExeRegex, exe_regex);
|
value_ref!(ExeRegex, exe_regex);
|
||||||
|
value_ref!(Tag, tag);
|
||||||
|
value_ref!(TagRegex, tag_regex);
|
||||||
value!(Uid, uid);
|
value!(Uid, uid);
|
||||||
value!(Pid, pid);
|
value!(Pid, pid);
|
||||||
bool!(Sandboxed, sandboxed);
|
bool!(Sandboxed, sandboxed);
|
||||||
|
|
|
||||||
|
|
@ -742,6 +742,14 @@
|
||||||
"exe-regex": {
|
"exe-regex": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Matches the `/proc/pid/exe` of the client with a regular expression."
|
"description": "Matches the `/proc/pid/exe` of the client with a regular expression."
|
||||||
|
},
|
||||||
|
"tag": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Matches the tag of the client verbatim."
|
||||||
|
},
|
||||||
|
"tag-regex": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Matches the tag of the client with a regular expression."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
@ -1283,6 +1291,10 @@
|
||||||
"privileged": {
|
"privileged": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "If `true`, the executable gets access to privileged wayland protocols.\n\nThe default is `false`.\n"
|
"description": "If `true`, the executable gets access to privileged wayland protocols.\n\nThe default is `false`.\n"
|
||||||
|
},
|
||||||
|
"tag": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Specifies a tag to apply to all spawned wayland connections.\n"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
|
||||||
|
|
@ -1242,6 +1242,18 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a string.
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
- `tag` (optional):
|
||||||
|
|
||||||
|
Matches the tag of the client verbatim.
|
||||||
|
|
||||||
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
- `tag-regex` (optional):
|
||||||
|
|
||||||
|
Matches the tag of the client with a regular expression.
|
||||||
|
|
||||||
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-ClientMatchExactly"></a>
|
<a name="types-ClientMatchExactly"></a>
|
||||||
### `ClientMatchExactly`
|
### `ClientMatchExactly`
|
||||||
|
|
@ -2711,6 +2723,12 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a boolean.
|
The value of this field should be a boolean.
|
||||||
|
|
||||||
|
- `tag` (optional):
|
||||||
|
|
||||||
|
Specifies a tag to apply to all spawned wayland connections.
|
||||||
|
|
||||||
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-FallbackOutputMode"></a>
|
<a name="types-FallbackOutputMode"></a>
|
||||||
### `FallbackOutputMode`
|
### `FallbackOutputMode`
|
||||||
|
|
|
||||||
|
|
@ -858,6 +858,11 @@ Exec:
|
||||||
If `true`, the executable gets access to privileged wayland protocols.
|
If `true`, the executable gets access to privileged wayland protocols.
|
||||||
|
|
||||||
The default is `false`.
|
The default is `false`.
|
||||||
|
tag:
|
||||||
|
kind: string
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Specifies a tag to apply to all spawned wayland connections.
|
||||||
|
|
||||||
|
|
||||||
SimpleActionName:
|
SimpleActionName:
|
||||||
|
|
@ -3823,6 +3828,14 @@ ClientMatch:
|
||||||
kind: string
|
kind: string
|
||||||
required: false
|
required: false
|
||||||
description: Matches the `/proc/pid/exe` of the client with a regular expression.
|
description: Matches the `/proc/pid/exe` of the client with a regular expression.
|
||||||
|
tag:
|
||||||
|
kind: string
|
||||||
|
required: false
|
||||||
|
description: Matches the tag of the client verbatim.
|
||||||
|
tag-regex:
|
||||||
|
kind: string
|
||||||
|
required: false
|
||||||
|
description: Matches the tag of the client with a regular expression.
|
||||||
|
|
||||||
|
|
||||||
ClientMatchExactly:
|
ClientMatchExactly:
|
||||||
|
|
|
||||||
9
wire/jay_acceptor_request.txt
Normal file
9
wire/jay_acceptor_request.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
request destroy { }
|
||||||
|
|
||||||
|
event done {
|
||||||
|
name: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event failed {
|
||||||
|
msg: str,
|
||||||
|
}
|
||||||
|
|
@ -47,3 +47,7 @@ event comm {
|
||||||
event exe {
|
event exe {
|
||||||
exe: str,
|
exe: str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event tag (since = 25) {
|
||||||
|
tag: str,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,11 @@ request create_tree_query (since = 18) {
|
||||||
id: id(jay_tree_query),
|
id: id(jay_tree_query),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request get_tagged_acceptor (since = 25) {
|
||||||
|
id: id(jay_acceptor_request),
|
||||||
|
tag: str,
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event client_id {
|
event client_id {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue