cli: add commands to inspect clients
This commit is contained in:
parent
5e3465d861
commit
bd04b09171
12 changed files with 557 additions and 16 deletions
10
src/cli.rs
10
src/cli.rs
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod clients;
|
||||||
mod color;
|
mod color;
|
||||||
mod color_management;
|
mod color_management;
|
||||||
mod damage_tracking;
|
mod damage_tracking;
|
||||||
|
|
@ -19,9 +20,9 @@ mod xwayland;
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{
|
cli::{
|
||||||
color_management::ColorManagementArgs, damage_tracking::DamageTrackingArgs,
|
clients::ClientsArgs, color_management::ColorManagementArgs,
|
||||||
idle::IdleCmd, input::InputArgs, randr::RandrArgs, reexec::ReexecArgs,
|
damage_tracking::DamageTrackingArgs, idle::IdleCmd, input::InputArgs, randr::RandrArgs,
|
||||||
xwayland::XwaylandArgs,
|
reexec::ReexecArgs, xwayland::XwaylandArgs,
|
||||||
},
|
},
|
||||||
compositor::start_compositor,
|
compositor::start_compositor,
|
||||||
format::{Format, ref_formats},
|
format::{Format, ref_formats},
|
||||||
|
|
@ -86,6 +87,8 @@ pub enum Cmd {
|
||||||
/// Replace the compositor by another process. (Only for development.)
|
/// Replace the compositor by another process. (Only for development.)
|
||||||
#[clap(hide = true)]
|
#[clap(hide = true)]
|
||||||
Reexec(ReexecArgs),
|
Reexec(ReexecArgs),
|
||||||
|
/// Inspect/manipulate the connected clients.
|
||||||
|
Clients(ClientsArgs),
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
RunTests,
|
RunTests,
|
||||||
}
|
}
|
||||||
|
|
@ -244,6 +247,7 @@ pub fn main() {
|
||||||
Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a),
|
Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a),
|
||||||
Cmd::Xwayland(a) => xwayland::main(cli.global, a),
|
Cmd::Xwayland(a) => xwayland::main(cli.global, a),
|
||||||
Cmd::ColorManagement(a) => color_management::main(cli.global, a),
|
Cmd::ColorManagement(a) => color_management::main(cli.global, a),
|
||||||
|
Cmd::Clients(a) => clients::main(cli.global, a),
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
Cmd::RunTests => crate::it::run_tests(),
|
Cmd::RunTests => crate::it::run_tests(),
|
||||||
Cmd::Reexec(a) => reexec::main(cli.global, a),
|
Cmd::Reexec(a) => reexec::main(cli.global, a),
|
||||||
|
|
|
||||||
243
src/cli/clients.rs
Normal file
243
src/cli/clients.rs
Normal file
|
|
@ -0,0 +1,243 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
cli::GlobalArgs,
|
||||||
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
|
wire::{JayClientQueryId, jay_client_query, jay_compositor},
|
||||||
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
|
clap::{Args, Subcommand},
|
||||||
|
std::{cell::RefCell, mem, rc::Rc},
|
||||||
|
uapi::c,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub struct ClientsArgs {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
cmd: Option<ClientsCmd>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum ClientsCmd {
|
||||||
|
/// Show information about clients.
|
||||||
|
Show(ShowArgs),
|
||||||
|
/// Disconnect a client.
|
||||||
|
Kill(KillArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct ShowArgs {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
cmd: ShowCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum ShowCmd {
|
||||||
|
/// Show all clients.
|
||||||
|
All,
|
||||||
|
/// Show a client with a given ID.
|
||||||
|
Id(ShowIdArgs),
|
||||||
|
/// Interactively select a window and show information about its client.
|
||||||
|
SelectWindow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct ShowIdArgs {
|
||||||
|
/// The ID of the client.
|
||||||
|
id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct KillArgs {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
cmd: KillCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum KillCmd {
|
||||||
|
/// Kill the client with a given ID.
|
||||||
|
Id(KillIdArgs),
|
||||||
|
/// Interactively select a window and kill its client.
|
||||||
|
SelectWindow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct KillIdArgs {
|
||||||
|
/// The ID of the client.
|
||||||
|
id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main(global: GlobalArgs, clients_args: ClientsArgs) {
|
||||||
|
with_tool_client(global.log_level.into(), |tc| async move {
|
||||||
|
let clients = Rc::new(Clients { tc: tc.clone() });
|
||||||
|
clients.run(clients_args).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Clients {
|
||||||
|
tc: Rc<ToolClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clients {
|
||||||
|
async fn run(&self, args: ClientsArgs) {
|
||||||
|
let tc = &self.tc;
|
||||||
|
let comp = tc.jay_compositor().await;
|
||||||
|
let cmd = args
|
||||||
|
.cmd
|
||||||
|
.unwrap_or(ClientsCmd::Show(ShowArgs { cmd: ShowCmd::All }));
|
||||||
|
match cmd {
|
||||||
|
ClientsCmd::Show(a) => {
|
||||||
|
let id = tc.id();
|
||||||
|
tc.send(jay_compositor::CreateClientQuery { self_id: comp, id });
|
||||||
|
match a.cmd {
|
||||||
|
ShowCmd::All => {
|
||||||
|
tc.send(jay_client_query::AddAll { self_id: id });
|
||||||
|
}
|
||||||
|
ShowCmd::Id(a) => {
|
||||||
|
tc.send(jay_client_query::AddId {
|
||||||
|
self_id: id,
|
||||||
|
id: a.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ShowCmd::SelectWindow => {
|
||||||
|
let client_id = tc.select_toplevel_client().await;
|
||||||
|
if client_id == 0 {
|
||||||
|
fatal!("Did not select a window");
|
||||||
|
}
|
||||||
|
tc.send(jay_client_query::AddId {
|
||||||
|
self_id: id,
|
||||||
|
id: client_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tc.send(jay_client_query::Execute { self_id: id });
|
||||||
|
let clients = handle_client_query(tc, id).await;
|
||||||
|
let mut clients = clients.values().collect::<Vec<_>>();
|
||||||
|
clients.sort_by_key(|c| c.id);
|
||||||
|
let mut prefix = " ".to_string();
|
||||||
|
let mut printer = ClientPrinter {
|
||||||
|
prefix: &mut prefix,
|
||||||
|
};
|
||||||
|
for client in clients {
|
||||||
|
println!("- client:");
|
||||||
|
printer.print_client(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientsCmd::Kill(a) => match a.cmd {
|
||||||
|
KillCmd::Id(id) => {
|
||||||
|
tc.send(jay_compositor::KillClient {
|
||||||
|
self_id: comp,
|
||||||
|
id: id.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
KillCmd::SelectWindow => {
|
||||||
|
let client_id = tc.select_toplevel_client().await;
|
||||||
|
if client_id == 0 {
|
||||||
|
fatal!("Did not select a window");
|
||||||
|
}
|
||||||
|
tc.send(jay_compositor::KillClient {
|
||||||
|
self_id: comp,
|
||||||
|
id: client_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tc.round_trip().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Client {
|
||||||
|
pub id: u64,
|
||||||
|
pub sandboxed: bool,
|
||||||
|
pub sandbox_engine: Option<String>,
|
||||||
|
pub sandbox_app_id: Option<String>,
|
||||||
|
pub sandbox_instance_id: Option<String>,
|
||||||
|
pub uid: Option<c::uid_t>,
|
||||||
|
pub pid: Option<c::pid_t>,
|
||||||
|
pub is_xwayland: bool,
|
||||||
|
pub comm: Option<String>,
|
||||||
|
pub exe: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_client_query(
|
||||||
|
tl: &Rc<ToolClient>,
|
||||||
|
id: JayClientQueryId,
|
||||||
|
) -> AHashMap<u64, Client> {
|
||||||
|
use jay_client_query::*;
|
||||||
|
let c = Rc::new(RefCell::new(Vec::<Client>::new()));
|
||||||
|
macro_rules! last {
|
||||||
|
($c:ident) => {
|
||||||
|
$c.borrow_mut().last_mut().unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Start::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
c.borrow_mut().push(Client::default());
|
||||||
|
last!(c).id = event.id;
|
||||||
|
});
|
||||||
|
Sandboxed::handle(tl, id, c.clone(), |c, _event| {
|
||||||
|
last!(c).sandboxed = true;
|
||||||
|
});
|
||||||
|
SandboxEngine::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
last!(c).sandbox_engine = Some(event.engine.to_string());
|
||||||
|
});
|
||||||
|
SandboxAppId::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
last!(c).sandbox_app_id = Some(event.app_id.to_string());
|
||||||
|
});
|
||||||
|
SandboxInstanceId::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
last!(c).sandbox_instance_id = Some(event.instance_id.to_string());
|
||||||
|
});
|
||||||
|
Uid::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
last!(c).uid = Some(event.uid);
|
||||||
|
});
|
||||||
|
Pid::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
last!(c).pid = Some(event.pid);
|
||||||
|
});
|
||||||
|
IsXwayland::handle(tl, id, c.clone(), |c, _event| {
|
||||||
|
last!(c).is_xwayland = true;
|
||||||
|
});
|
||||||
|
Comm::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
last!(c).comm = Some(event.comm.to_string());
|
||||||
|
});
|
||||||
|
Exe::handle(tl, id, c.clone(), |c, event| {
|
||||||
|
last!(c).exe = Some(event.exe.to_string());
|
||||||
|
});
|
||||||
|
tl.round_trip().await;
|
||||||
|
mem::take(&mut *c.borrow_mut())
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| (c.id, c))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClientPrinter<'a> {
|
||||||
|
pub prefix: &'a mut String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientPrinter<'_> {
|
||||||
|
pub fn print_client(&mut self, c: &Client) {
|
||||||
|
let p = &self.prefix;
|
||||||
|
macro_rules! opt {
|
||||||
|
($field:ident, $pretty:expr) => {
|
||||||
|
if let Some(v) = &c.$field {
|
||||||
|
println!("{p}{}: {}", $pretty, v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! bol {
|
||||||
|
($field:ident, $pretty:expr) => {
|
||||||
|
if c.$field {
|
||||||
|
println!("{p}{}", $pretty);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
println!("{p}id: {}", c.id);
|
||||||
|
bol!(sandboxed, "sandboxed");
|
||||||
|
opt!(sandbox_engine, "sandbox engine");
|
||||||
|
opt!(sandbox_app_id, "sandbox app id");
|
||||||
|
opt!(sandbox_instance_id, "sandbox instance id");
|
||||||
|
opt!(uid, "uid");
|
||||||
|
opt!(pid, "pid");
|
||||||
|
bol!(is_xwayland, "xwayland");
|
||||||
|
opt!(comm, "comm");
|
||||||
|
opt!(exe, "exe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ pub mod ext_output_image_capture_source_manager_v1;
|
||||||
pub mod ext_session_lock_manager_v1;
|
pub mod ext_session_lock_manager_v1;
|
||||||
pub mod ext_session_lock_v1;
|
pub mod ext_session_lock_v1;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
|
pub mod jay_client_query;
|
||||||
pub mod jay_color_management;
|
pub mod jay_color_management;
|
||||||
pub mod jay_compositor;
|
pub mod jay_compositor;
|
||||||
pub mod jay_damage_tracking;
|
pub mod jay_damage_tracking;
|
||||||
|
|
|
||||||
141
src/ifs/jay_client_query.rs
Normal file
141
src/ifs/jay_client_query.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError, ClientId},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
utils::copyhashmap::CopyHashMap,
|
||||||
|
wire::{
|
||||||
|
JayClientQueryId,
|
||||||
|
jay_client_query::{
|
||||||
|
AddAll, AddId, Comm, Destroy, Done, End, Exe, Execute, IsXwayland,
|
||||||
|
JayClientQueryRequestHandler, Pid, SandboxAppId, SandboxEngine, SandboxInstanceId,
|
||||||
|
Sandboxed, Start, Uid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct JayClientQuery {
|
||||||
|
pub id: JayClientQueryId,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
ids: CopyHashMap<ClientId, ()>,
|
||||||
|
all: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayClientQuery {
|
||||||
|
pub fn new(client: &Rc<Client>, id: JayClientQueryId, version: Version) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
ids: Default::default(),
|
||||||
|
all: Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayClientQueryRequestHandler for JayClientQuery {
|
||||||
|
type Error = JayClientQueryError;
|
||||||
|
|
||||||
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(&self, _req: Execute, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let handle_client = |client: &Rc<Client>| {
|
||||||
|
self.client.event(Start {
|
||||||
|
self_id: self.id,
|
||||||
|
id: client.id.raw(),
|
||||||
|
});
|
||||||
|
if !client.is_xwayland {
|
||||||
|
self.client.event(Uid {
|
||||||
|
self_id: self.id,
|
||||||
|
uid: client.pid_info.uid,
|
||||||
|
});
|
||||||
|
self.client.event(Pid {
|
||||||
|
self_id: self.id,
|
||||||
|
pid: client.pid_info.pid,
|
||||||
|
});
|
||||||
|
self.client.event(Comm {
|
||||||
|
self_id: self.id,
|
||||||
|
comm: &client.pid_info.comm,
|
||||||
|
});
|
||||||
|
self.client.event(Exe {
|
||||||
|
self_id: self.id,
|
||||||
|
exe: &client.pid_info.exe,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if client.acceptor.sandboxed {
|
||||||
|
self.client.event(Sandboxed { self_id: self.id });
|
||||||
|
}
|
||||||
|
if client.is_xwayland {
|
||||||
|
self.client.event(IsXwayland { self_id: self.id });
|
||||||
|
}
|
||||||
|
if let Some(engine) = &client.acceptor.sandbox_engine {
|
||||||
|
self.client.event(SandboxEngine {
|
||||||
|
self_id: self.id,
|
||||||
|
engine,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(app_id) = &client.acceptor.app_id {
|
||||||
|
self.client.event(SandboxAppId {
|
||||||
|
self_id: self.id,
|
||||||
|
app_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(instance_id) = &client.acceptor.instance_id {
|
||||||
|
self.client.event(SandboxInstanceId {
|
||||||
|
self_id: self.id,
|
||||||
|
instance_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.client.event(End { self_id: self.id });
|
||||||
|
};
|
||||||
|
if self.all.get() {
|
||||||
|
for client in self.client.state.clients.clients.borrow().values() {
|
||||||
|
handle_client(&client.data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for &id in self.ids.lock().keys() {
|
||||||
|
let Ok(client) = self.client.state.clients.get(id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
handle_client(&client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.client.event(Done { self_id: self.id });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_all(&self, _req: AddAll, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.all.set(true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_id(&self, req: AddId, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.ids.set(ClientId::from_raw(req.id), ());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = JayClientQuery;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for JayClientQuery {}
|
||||||
|
|
||||||
|
simple_add_obj!(JayClientQuery);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum JayClientQueryError {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
}
|
||||||
|
efrom!(JayClientQueryError, ClientError);
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::CliLogLevel,
|
cli::CliLogLevel,
|
||||||
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError},
|
client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError, ClientId},
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
ifs::{
|
ifs::{
|
||||||
|
jay_client_query::JayClientQuery,
|
||||||
jay_color_management::JayColorManagement,
|
jay_color_management::JayColorManagement,
|
||||||
jay_ei_session_builder::JayEiSessionBuilder,
|
jay_ei_session_builder::JayEiSessionBuilder,
|
||||||
jay_idle::JayIdle,
|
jay_idle::JayIdle,
|
||||||
|
|
@ -26,7 +27,10 @@ use {
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
screenshoter::take_screenshot,
|
screenshoter::take_screenshot,
|
||||||
utils::{errorfmt::ErrorFmt, toplevel_identifier::ToplevelIdentifier},
|
utils::{errorfmt::ErrorFmt, toplevel_identifier::ToplevelIdentifier},
|
||||||
wire::{JayCompositorId, JayScreenshotId, jay_compositor::*},
|
wire::{
|
||||||
|
JayCompositorId, JayScreenshotId,
|
||||||
|
jay_compositor::{self, *},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
bstr::ByteSlice,
|
bstr::ByteSlice,
|
||||||
log::Level,
|
log::Level,
|
||||||
|
|
@ -74,7 +78,7 @@ impl Global for JayCompositorGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
17
|
18
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_caps(&self) -> ClientCaps {
|
fn required_caps(&self) -> ClientCaps {
|
||||||
|
|
@ -223,7 +227,7 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_client_id(&self, _req: GetClientId, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn get_client_id(&self, _req: GetClientId, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
self.client.event(ClientId {
|
self.client.event(jay_compositor::ClientId {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
client_id: self.client.id.raw(),
|
client_id: self.client.id.raw(),
|
||||||
});
|
});
|
||||||
|
|
@ -367,7 +371,6 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_toplevel(&self, req: SelectToplevel, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn select_toplevel(&self, req: SelectToplevel, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
let seat = self.client.lookup(req.seat)?;
|
|
||||||
let obj = JaySelectToplevel::new(&self.client, req.id, self.version);
|
let obj = JaySelectToplevel::new(&self.client, req.id, self.version);
|
||||||
track!(self.client, obj);
|
track!(self.client, obj);
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
|
|
@ -375,12 +378,22 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
tl: Default::default(),
|
tl: Default::default(),
|
||||||
jst: obj.clone(),
|
jst: obj.clone(),
|
||||||
};
|
};
|
||||||
seat.global.select_toplevel(selector);
|
let seat = if req.seat.is_none() {
|
||||||
|
match self.client.state.seat_queue.last() {
|
||||||
|
Some(s) => s.deref().clone(),
|
||||||
|
None => {
|
||||||
|
obj.done(None);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.client.lookup(req.seat)?.global.clone()
|
||||||
|
};
|
||||||
|
seat.select_toplevel(selector);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_workspace(&self, req: SelectWorkspace, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn select_workspace(&self, req: SelectWorkspace, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
let seat = self.client.lookup(req.seat)?;
|
|
||||||
let obj = Rc::new(JaySelectWorkspace {
|
let obj = Rc::new(JaySelectWorkspace {
|
||||||
id: req.id,
|
id: req.id,
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
|
|
@ -393,7 +406,15 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
ws: Default::default(),
|
ws: Default::default(),
|
||||||
jsw: obj.clone(),
|
jsw: obj.clone(),
|
||||||
};
|
};
|
||||||
seat.global.select_workspace(selector);
|
let seat = if req.seat.is_none() {
|
||||||
|
match self.client.state.seat_queue.last() {
|
||||||
|
Some(s) => s.deref().clone(),
|
||||||
|
None => return Ok(()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.client.lookup(req.seat)?.global.clone()
|
||||||
|
};
|
||||||
|
seat.select_workspace(selector);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -470,6 +491,22 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_client_query(
|
||||||
|
&self,
|
||||||
|
req: CreateClientQuery,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let obj = Rc::new(JayClientQuery::new(&self.client, req.id, self.version));
|
||||||
|
track!(self.client, obj);
|
||||||
|
self.client.add_client_obj(&obj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill_client(&self, req: KillClient, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.client.state.clients.kill(ClientId::from_raw(req.id));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
ifs::{
|
ifs::{
|
||||||
jay_toplevel::{ID_SINCE, JayToplevel},
|
jay_toplevel::{CLIENT_ID_SINCE, ID_SINCE, JayToplevel},
|
||||||
wl_seat::ToplevelSelector,
|
wl_seat::ToplevelSelector,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
|
|
@ -78,6 +78,9 @@ impl JaySelectToplevel {
|
||||||
self.send_done(jtl.id);
|
self.send_done(jtl.id);
|
||||||
if jtl.version >= ID_SINCE {
|
if jtl.version >= ID_SINCE {
|
||||||
jtl.send_id();
|
jtl.send_id();
|
||||||
|
if jtl.version >= CLIENT_ID_SINCE {
|
||||||
|
jtl.send_client_id();
|
||||||
|
}
|
||||||
jtl.send_done();
|
jtl.send_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ID_SINCE: Version = Version(12);
|
pub const ID_SINCE: Version = Version(12);
|
||||||
|
pub const CLIENT_ID_SINCE: Version = Version(18);
|
||||||
|
|
||||||
pub struct JayToplevel {
|
pub struct JayToplevel {
|
||||||
pub id: JayToplevelId,
|
pub id: JayToplevelId,
|
||||||
|
|
@ -47,6 +48,15 @@ impl JayToplevel {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_client_id(&self) {
|
||||||
|
if let Some(cl) = &self.toplevel.tl_data().client {
|
||||||
|
self.client.event(ClientId {
|
||||||
|
self_id: self.id,
|
||||||
|
id: cl.id.raw(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send_done(&self) {
|
pub fn send_done(&self) {
|
||||||
self.client.event(Done { self_id: self.id })
|
self.client.event(Done { self_id: self.id })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ use {
|
||||||
wheel::{Wheel, WheelError},
|
wheel::{Wheel, WheelError},
|
||||||
wire::{
|
wire::{
|
||||||
JayCompositor, JayCompositorId, JayDamageTracking, JayDamageTrackingId, WlCallbackId,
|
JayCompositor, JayCompositorId, JayDamageTracking, JayDamageTrackingId, WlCallbackId,
|
||||||
WlRegistryId, wl_callback, wl_display, wl_registry,
|
WlRegistryId, WlSeatId, jay_compositor, jay_select_toplevel, jay_toplevel, wl_callback,
|
||||||
|
wl_display, wl_registry,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -63,8 +64,8 @@ pub enum ToolClientError {
|
||||||
UnalignedMessage,
|
UnalignedMessage,
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
BufFdError(#[from] BufFdError),
|
BufFdError(#[from] BufFdError),
|
||||||
#[error("The size of the message is not a multiple of 4")]
|
#[error("Could not parse a message of type {}", .0)]
|
||||||
Parsing(&'static str, MsgParserError),
|
Parsing(&'static str, #[source] MsgParserError),
|
||||||
#[error("Could not read from the compositor")]
|
#[error("Could not read from the compositor")]
|
||||||
Read(#[source] BufFdError),
|
Read(#[source] BufFdError),
|
||||||
#[error("Could not write to the compositor")]
|
#[error("Could not write to the compositor")]
|
||||||
|
|
@ -195,6 +196,7 @@ impl ToolClient {
|
||||||
fatal!("The compositor returned a fatal error: {}", val.message);
|
fatal!("The compositor returned a fatal error: {}", val.message);
|
||||||
});
|
});
|
||||||
wl_display::DeleteId::handle(&slf, WL_DISPLAY_ID, slf.clone(), |tc, val| {
|
wl_display::DeleteId::handle(&slf, WL_DISPLAY_ID, slf.clone(), |tc, val| {
|
||||||
|
tc.handlers.borrow_mut().remove(&ObjectId::from_raw(val.id));
|
||||||
tc.obj_ids.borrow_mut().release(val.id);
|
tc.obj_ids.borrow_mut().release(val.id);
|
||||||
});
|
});
|
||||||
slf.incoming.set(Some(
|
slf.incoming.set(Some(
|
||||||
|
|
@ -332,7 +334,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(17),
|
version: s.jay_compositor.1.min(18),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
});
|
});
|
||||||
self.jay_compositor.set(Some(id));
|
self.jay_compositor.set(Some(id));
|
||||||
|
|
@ -359,6 +361,41 @@ impl ToolClient {
|
||||||
self.jay_damage_tracking.set(Some(Some(id)));
|
self.jay_damage_tracking.set(Some(Some(id)));
|
||||||
Some(id)
|
Some(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn select_toplevel_client(self: &Rc<Self>) -> u64 {
|
||||||
|
let id = self.id();
|
||||||
|
self.send(jay_compositor::SelectToplevel {
|
||||||
|
self_id: self.jay_compositor().await,
|
||||||
|
id,
|
||||||
|
seat: WlSeatId::NONE,
|
||||||
|
});
|
||||||
|
let ae = Rc::new(AsyncEvent::default());
|
||||||
|
let client_id = Rc::new(Cell::new(0));
|
||||||
|
jay_select_toplevel::Done::handle(
|
||||||
|
self,
|
||||||
|
id,
|
||||||
|
(self.clone(), ae.clone(), client_id.clone()),
|
||||||
|
|(tc, ae, client_id), event| {
|
||||||
|
if event.id.is_some() {
|
||||||
|
jay_toplevel::ClientId::handle(
|
||||||
|
tc,
|
||||||
|
event.id,
|
||||||
|
client_id.clone(),
|
||||||
|
|client_id, event| {
|
||||||
|
client_id.set(event.id);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
jay_toplevel::Done::handle(tc, event.id, ae.clone(), |ae, _event| {
|
||||||
|
ae.trigger();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ae.trigger();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ae.triggered().await;
|
||||||
|
client_id.get()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Singletons {
|
pub struct Singletons {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ impl JayToplevelEventHandler for UsrJayToplevel {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_id(&self, _ev: ClientId, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn done(&self, _ev: Done, slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn done(&self, _ev: Done, slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
if let Some(owner) = self.owner.get() {
|
if let Some(owner) = self.owner.get() {
|
||||||
owner.done(slf);
|
owner.done(slf);
|
||||||
|
|
|
||||||
49
wire/jay_client_query.txt
Normal file
49
wire/jay_client_query.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
request destroy { }
|
||||||
|
|
||||||
|
request execute { }
|
||||||
|
|
||||||
|
request add_all { }
|
||||||
|
|
||||||
|
request add_id {
|
||||||
|
id: pod(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
event done { }
|
||||||
|
|
||||||
|
event start {
|
||||||
|
id: pod(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
event end { }
|
||||||
|
|
||||||
|
event sandboxed { }
|
||||||
|
|
||||||
|
event sandbox_engine {
|
||||||
|
engine: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event sandbox_app_id {
|
||||||
|
app_id: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event sandbox_instance_id {
|
||||||
|
instance_id: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event uid {
|
||||||
|
uid: pod(uapi::c::uid_t),
|
||||||
|
}
|
||||||
|
|
||||||
|
event pid {
|
||||||
|
pid: pod(uapi::c::pid_t),
|
||||||
|
}
|
||||||
|
|
||||||
|
event is_xwayland { }
|
||||||
|
|
||||||
|
event comm {
|
||||||
|
comm: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event exe {
|
||||||
|
exe: str,
|
||||||
|
}
|
||||||
|
|
@ -109,6 +109,14 @@ request reexec (since = 17) {
|
||||||
id: id(jay_reexec),
|
id: id(jay_reexec),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request create_client_query (since = 18) {
|
||||||
|
id: id(jay_client_query),
|
||||||
|
}
|
||||||
|
|
||||||
|
request kill_client (since = 18) {
|
||||||
|
id: pod(u64),
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event client_id {
|
event client_id {
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,9 @@ event id (since = 12) {
|
||||||
id: str,
|
id: str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event client_id (since = 18) {
|
||||||
|
id: pod(u64),
|
||||||
|
}
|
||||||
|
|
||||||
event done (since = 12) {
|
event done (since = 12) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue