cli: add commands to inspect the tree
This commit is contained in:
parent
bd04b09171
commit
38d7a60d00
14 changed files with 1072 additions and 8 deletions
|
|
@ -14,6 +14,7 @@ mod run_privileged;
|
||||||
pub mod screenshot;
|
pub mod screenshot;
|
||||||
mod seat_test;
|
mod seat_test;
|
||||||
mod set_log_level;
|
mod set_log_level;
|
||||||
|
mod tree;
|
||||||
mod unlock;
|
mod unlock;
|
||||||
mod xwayland;
|
mod xwayland;
|
||||||
|
|
||||||
|
|
@ -22,7 +23,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, xwayland::XwaylandArgs,
|
reexec::ReexecArgs, tree::TreeArgs, xwayland::XwaylandArgs,
|
||||||
},
|
},
|
||||||
compositor::start_compositor,
|
compositor::start_compositor,
|
||||||
format::{Format, ref_formats},
|
format::{Format, ref_formats},
|
||||||
|
|
@ -89,6 +90,8 @@ pub enum Cmd {
|
||||||
Reexec(ReexecArgs),
|
Reexec(ReexecArgs),
|
||||||
/// Inspect/manipulate the connected clients.
|
/// Inspect/manipulate the connected clients.
|
||||||
Clients(ClientsArgs),
|
Clients(ClientsArgs),
|
||||||
|
/// Inspect the surface tree.
|
||||||
|
Tree(TreeArgs),
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
RunTests,
|
RunTests,
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +251,7 @@ pub fn main() {
|
||||||
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),
|
Cmd::Clients(a) => clients::main(cli.global, a),
|
||||||
|
Cmd::Tree(a) => tree::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),
|
||||||
|
|
|
||||||
419
src/cli/tree.rs
Normal file
419
src/cli/tree.rs
Normal file
|
|
@ -0,0 +1,419 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
cli::{
|
||||||
|
GlobalArgs,
|
||||||
|
clients::{Client, ClientPrinter, handle_client_query},
|
||||||
|
},
|
||||||
|
ifs::jay_tree_query::{
|
||||||
|
TREE_TY_CONTAINER, TREE_TY_DISPLAY, TREE_TY_FLOAT, TREE_TY_LAYER_SURFACE,
|
||||||
|
TREE_TY_LOCK_SURFACE, TREE_TY_OUTPUT, TREE_TY_PLACEHOLDER, TREE_TY_WORKSPACE,
|
||||||
|
TREE_TY_X_WINDOW, TREE_TY_XDG_POPUP, TREE_TY_XDG_TOPLEVEL,
|
||||||
|
},
|
||||||
|
rect::Rect,
|
||||||
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
|
wire::{JayCompositorId, JayTreeQueryId, jay_client_query, jay_compositor, jay_tree_query},
|
||||||
|
},
|
||||||
|
ahash::{AHashMap, AHashSet},
|
||||||
|
clap::{Args, Subcommand},
|
||||||
|
isnt::std_1::primitive::IsntSliceExt,
|
||||||
|
std::{cell::RefCell, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub struct TreeArgs {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
cmd: TreeCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum TreeCmd {
|
||||||
|
/// Query the tree.
|
||||||
|
Query(QueryArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct QueryArgs {
|
||||||
|
/// Whether to perform a recursive query.
|
||||||
|
#[arg(short, long)]
|
||||||
|
recursive: bool,
|
||||||
|
/// Whether to repeatedly print details of the same client.
|
||||||
|
#[arg(long)]
|
||||||
|
all_clients: bool,
|
||||||
|
#[clap(subcommand)]
|
||||||
|
cmd: QueryCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum QueryCmd {
|
||||||
|
/// Query the entire display.
|
||||||
|
Root,
|
||||||
|
/// Query a workspace by name.
|
||||||
|
WorkspaceName(QueryWorkspaceNameArgs),
|
||||||
|
/// Interactively select a workspace to query.
|
||||||
|
SelectWorkspace,
|
||||||
|
/// Interactively select a window to query.
|
||||||
|
SelectWindow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct QueryWorkspaceNameArgs {
|
||||||
|
/// The name of the workspace.
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main(global: GlobalArgs, tree_args: TreeArgs) {
|
||||||
|
with_tool_client(global.log_level.into(), |tc| async move {
|
||||||
|
let comp = tc.jay_compositor().await;
|
||||||
|
let tree = Rc::new(Tree {
|
||||||
|
tc: tc.clone(),
|
||||||
|
comp,
|
||||||
|
});
|
||||||
|
tree.run(tree_args).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Tree {
|
||||||
|
tc: Rc<ToolClient>,
|
||||||
|
comp: JayCompositorId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tree {
|
||||||
|
async fn run(&self, args: TreeArgs) {
|
||||||
|
match &args.cmd {
|
||||||
|
TreeCmd::Query(a) => self.query(a).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn query(&self, args: &QueryArgs) {
|
||||||
|
let id = self.tc.id();
|
||||||
|
self.tc.send(jay_compositor::CreateTreeQuery {
|
||||||
|
self_id: self.comp,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
let mut query = Query {
|
||||||
|
tree: self,
|
||||||
|
tc: &self.tc,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
query.run(args).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Query<'a> {
|
||||||
|
tree: &'a Tree,
|
||||||
|
tc: &'a Rc<ToolClient>,
|
||||||
|
id: JayTreeQueryId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Queried {
|
||||||
|
not_found: bool,
|
||||||
|
roots: Vec<Node>,
|
||||||
|
stack: Vec<Node>,
|
||||||
|
client_ids: AHashSet<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Node {
|
||||||
|
ty: u32,
|
||||||
|
children: Vec<Node>,
|
||||||
|
position: Option<Rect>,
|
||||||
|
toplevel_id: Option<String>,
|
||||||
|
client: Option<u64>,
|
||||||
|
title: Option<String>,
|
||||||
|
app_id: Option<String>,
|
||||||
|
tag: Option<String>,
|
||||||
|
x_class: Option<String>,
|
||||||
|
x_instance: Option<String>,
|
||||||
|
x_role: Option<String>,
|
||||||
|
workspace: Option<String>,
|
||||||
|
placeholder_for: Option<String>,
|
||||||
|
floating: bool,
|
||||||
|
visible: bool,
|
||||||
|
urgent: bool,
|
||||||
|
fullscreen: bool,
|
||||||
|
output: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Query<'_> {
|
||||||
|
async fn run(&mut self, args: &QueryArgs) {
|
||||||
|
match &args.cmd {
|
||||||
|
QueryCmd::Root => {
|
||||||
|
self.tc.send(SetRootDisplay { self_id: self.id });
|
||||||
|
}
|
||||||
|
QueryCmd::WorkspaceName(a) => {
|
||||||
|
self.tc.send(SetRootWorkspaceName {
|
||||||
|
self_id: self.id,
|
||||||
|
workspace: &a.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
QueryCmd::SelectWorkspace => {
|
||||||
|
let id = self.tc.select_workspace().await;
|
||||||
|
if id.is_none() {
|
||||||
|
fatal!("Workspace selection failed");
|
||||||
|
}
|
||||||
|
self.tc.send(SetRootWorkspace {
|
||||||
|
self_id: self.id,
|
||||||
|
workspace: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
QueryCmd::SelectWindow => {
|
||||||
|
let id = self.tc.select_toplevel().await;
|
||||||
|
if id.is_none() {
|
||||||
|
fatal!("Window selection failed");
|
||||||
|
}
|
||||||
|
self.tc.send(SetRootToplevel {
|
||||||
|
self_id: self.id,
|
||||||
|
toplevel: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let tl = self.tc;
|
||||||
|
let id = self.id;
|
||||||
|
let d = Rc::new(RefCell::new(Queried::default()));
|
||||||
|
use jay_tree_query::*;
|
||||||
|
macro_rules! last {
|
||||||
|
($d:ident, $n:ident) => {
|
||||||
|
let $d = &mut *$d.borrow_mut();
|
||||||
|
let $n = $d.stack.last_mut().unwrap();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
NotFound::handle(tl, id, d.clone(), |d, _event| {
|
||||||
|
d.borrow_mut().not_found = true;
|
||||||
|
});
|
||||||
|
End::handle(tl, id, d.clone(), |d, _event| {
|
||||||
|
let d = &mut *d.borrow_mut();
|
||||||
|
let n = d.stack.pop().unwrap();
|
||||||
|
if let Some(p) = d.stack.last_mut() {
|
||||||
|
p.children.push(n);
|
||||||
|
} else {
|
||||||
|
d.roots.push(n);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Position::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.position = Rect::new_sized(event.x, event.y, event.w, event.h);
|
||||||
|
});
|
||||||
|
Start::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
let d = &mut *d.borrow_mut();
|
||||||
|
let node = Node {
|
||||||
|
ty: event.ty,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
d.stack.push(node);
|
||||||
|
});
|
||||||
|
OutputName::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.output = Some(event.name.to_string());
|
||||||
|
});
|
||||||
|
WorkspaceName::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.workspace = Some(event.name.to_string());
|
||||||
|
});
|
||||||
|
ToplevelId::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.toplevel_id = Some(event.id.to_string());
|
||||||
|
});
|
||||||
|
ClientId::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.client = Some(event.id);
|
||||||
|
d.client_ids.insert(event.id);
|
||||||
|
});
|
||||||
|
Title::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.title = Some(event.title.to_string());
|
||||||
|
});
|
||||||
|
AppId::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.app_id = Some(event.app_id.to_string());
|
||||||
|
});
|
||||||
|
Floating::handle(tl, id, d.clone(), |d, _event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.floating = true;
|
||||||
|
});
|
||||||
|
Visible::handle(tl, id, d.clone(), |d, _event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.visible = true;
|
||||||
|
});
|
||||||
|
Urgent::handle(tl, id, d.clone(), |d, _event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.urgent = true;
|
||||||
|
});
|
||||||
|
Fullscreen::handle(tl, id, d.clone(), |d, _event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.fullscreen = true;
|
||||||
|
});
|
||||||
|
Tag::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.tag = Some(event.tag.to_string());
|
||||||
|
});
|
||||||
|
XClass::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.x_class = Some(event.class.to_string());
|
||||||
|
});
|
||||||
|
XInstance::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.x_instance = Some(event.instance.to_string());
|
||||||
|
});
|
||||||
|
XRole::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.x_role = Some(event.role.to_string());
|
||||||
|
});
|
||||||
|
Workspace::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.workspace = Some(event.name.to_string());
|
||||||
|
});
|
||||||
|
PlaceholderFor::handle(tl, id, d.clone(), |d, event| {
|
||||||
|
last!(d, n);
|
||||||
|
n.placeholder_for = Some(event.id.to_string());
|
||||||
|
});
|
||||||
|
if args.recursive {
|
||||||
|
tl.send(SetRecursive {
|
||||||
|
self_id: id,
|
||||||
|
recursive: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tl.send(Execute { self_id: id });
|
||||||
|
tl.round_trip().await;
|
||||||
|
let clients = {
|
||||||
|
let id = tl.id();
|
||||||
|
tl.send(jay_compositor::CreateClientQuery {
|
||||||
|
self_id: self.tree.comp,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
use jay_client_query::*;
|
||||||
|
for &client in &d.borrow().client_ids {
|
||||||
|
tl.send(AddId {
|
||||||
|
self_id: id,
|
||||||
|
id: client,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tl.send(Execute { self_id: id });
|
||||||
|
handle_client_query(tl, id).await
|
||||||
|
};
|
||||||
|
let mut printer = Printer {
|
||||||
|
clients,
|
||||||
|
printed_clients: Default::default(),
|
||||||
|
verbose: args.all_clients,
|
||||||
|
prefix: "".to_string(),
|
||||||
|
output_depth: 0,
|
||||||
|
workspace_depth: 0,
|
||||||
|
};
|
||||||
|
for node in &d.borrow().roots {
|
||||||
|
printer.print(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Printer {
|
||||||
|
clients: AHashMap<u64, Client>,
|
||||||
|
printed_clients: AHashSet<u64>,
|
||||||
|
verbose: bool,
|
||||||
|
prefix: String,
|
||||||
|
output_depth: u32,
|
||||||
|
workspace_depth: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Printer {
|
||||||
|
fn print(&mut self, node: &Node) {
|
||||||
|
let p = &self.prefix;
|
||||||
|
'ty: {
|
||||||
|
let n = match node.ty {
|
||||||
|
TREE_TY_DISPLAY => "display",
|
||||||
|
TREE_TY_OUTPUT => "output",
|
||||||
|
TREE_TY_WORKSPACE => "workspace",
|
||||||
|
TREE_TY_FLOAT => "float",
|
||||||
|
TREE_TY_CONTAINER => "container",
|
||||||
|
TREE_TY_PLACEHOLDER => "placeholder",
|
||||||
|
TREE_TY_XDG_TOPLEVEL => "xdg-toplevel",
|
||||||
|
TREE_TY_X_WINDOW => "x-window",
|
||||||
|
TREE_TY_XDG_POPUP => "xdg-popup",
|
||||||
|
TREE_TY_LAYER_SURFACE => "layer-surface",
|
||||||
|
TREE_TY_LOCK_SURFACE => "lock-surface",
|
||||||
|
_ => {
|
||||||
|
println!("{p}- unknown ({}):", node.ty);
|
||||||
|
break 'ty;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("{p}- {n}:");
|
||||||
|
}
|
||||||
|
macro_rules! opt {
|
||||||
|
($field:ident, $pretty:expr) => {
|
||||||
|
if let Some(v) = &node.$field {
|
||||||
|
println!("{p} {}: {}", $pretty, v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! bol {
|
||||||
|
($field:ident, $pretty:expr) => {
|
||||||
|
if node.$field {
|
||||||
|
println!("{p} {}", $pretty);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if node.ty == TREE_TY_OUTPUT {
|
||||||
|
opt!(output, "name");
|
||||||
|
}
|
||||||
|
if node.ty == TREE_TY_WORKSPACE {
|
||||||
|
opt!(workspace, "name");
|
||||||
|
}
|
||||||
|
opt!(toplevel_id, "id");
|
||||||
|
opt!(placeholder_for, "placeholder-for");
|
||||||
|
if let Some(r) = node.position {
|
||||||
|
println!(
|
||||||
|
"{p} pos: {}x{} + {}x{}",
|
||||||
|
r.x1(),
|
||||||
|
r.y1(),
|
||||||
|
r.width(),
|
||||||
|
r.height()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(client_id) = node.client {
|
||||||
|
let client = self.clients.get(&client_id);
|
||||||
|
if client.is_some() && (self.printed_clients.insert(client_id) || self.verbose) {
|
||||||
|
println!("{p} client:");
|
||||||
|
let mut prefix = format!("{} ", p);
|
||||||
|
let mut cp = ClientPrinter {
|
||||||
|
prefix: &mut prefix,
|
||||||
|
};
|
||||||
|
cp.print_client(client.unwrap());
|
||||||
|
} else {
|
||||||
|
println!("{p} client: {client_id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt!(title, "title");
|
||||||
|
opt!(app_id, "app-id");
|
||||||
|
opt!(tag, "tag");
|
||||||
|
opt!(x_class, "x-class");
|
||||||
|
opt!(x_instance, "x-instance");
|
||||||
|
opt!(x_role, "x-role");
|
||||||
|
if self.workspace_depth == 0 && node.ty != TREE_TY_WORKSPACE {
|
||||||
|
opt!(workspace, "workspace");
|
||||||
|
}
|
||||||
|
if self.workspace_depth == 0 && self.output_depth == 0 && node.ty != TREE_TY_OUTPUT {
|
||||||
|
opt!(output, "output");
|
||||||
|
}
|
||||||
|
bol!(floating, "floating");
|
||||||
|
bol!(visible, "visible");
|
||||||
|
bol!(urgent, "urgent");
|
||||||
|
bol!(fullscreen, "fullscreen");
|
||||||
|
if node.children.is_not_empty() {
|
||||||
|
let (od, wd) = match node.ty {
|
||||||
|
TREE_TY_OUTPUT => (1, 0),
|
||||||
|
TREE_TY_WORKSPACE => (0, 1),
|
||||||
|
_ => (0, 0),
|
||||||
|
};
|
||||||
|
self.output_depth += od;
|
||||||
|
self.workspace_depth += wd;
|
||||||
|
println!("{p} children:");
|
||||||
|
let len = self.prefix.len();
|
||||||
|
self.prefix.push_str(" ");
|
||||||
|
for child in &node.children {
|
||||||
|
self.print(child);
|
||||||
|
}
|
||||||
|
self.prefix.truncate(len);
|
||||||
|
self.output_depth -= od;
|
||||||
|
self.workspace_depth -= wd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ pub mod jay_select_toplevel;
|
||||||
pub mod jay_select_workspace;
|
pub mod jay_select_workspace;
|
||||||
pub mod jay_toplevel;
|
pub mod jay_toplevel;
|
||||||
pub mod jay_tray_v1;
|
pub mod jay_tray_v1;
|
||||||
|
pub mod jay_tree_query;
|
||||||
pub mod jay_workspace;
|
pub mod jay_workspace;
|
||||||
pub mod jay_workspace_watcher;
|
pub mod jay_workspace_watcher;
|
||||||
pub mod jay_xwayland;
|
pub mod jay_xwayland;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use {
|
||||||
jay_seat_events::JaySeatEvents,
|
jay_seat_events::JaySeatEvents,
|
||||||
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
|
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
|
||||||
jay_select_workspace::{JaySelectWorkspace, JayWorkspaceSelector},
|
jay_select_workspace::{JaySelectWorkspace, JayWorkspaceSelector},
|
||||||
|
jay_tree_query::JayTreeQuery,
|
||||||
jay_workspace_watcher::JayWorkspaceWatcher,
|
jay_workspace_watcher::JayWorkspaceWatcher,
|
||||||
jay_xwayland::JayXwayland,
|
jay_xwayland::JayXwayland,
|
||||||
},
|
},
|
||||||
|
|
@ -507,6 +508,13 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
self.client.state.clients.kill(ClientId::from_raw(req.id));
|
self.client.state.clients.kill(ClientId::from_raw(req.id));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_tree_query(&self, req: CreateTreeQuery, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let obj = Rc::new(JayTreeQuery::new(&self.client, req.id, self.version));
|
||||||
|
track!(self.client, obj);
|
||||||
|
self.client.add_client_obj(&obj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
458
src/ifs/jay_tree_query.rs
Normal file
458
src/ifs/jay_tree_query.rs
Normal file
|
|
@ -0,0 +1,458 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
globals::GlobalBase,
|
||||||
|
ifs::wl_surface::{
|
||||||
|
ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
|
||||||
|
x_surface::xwindow::Xwindow,
|
||||||
|
xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::XdgToplevel},
|
||||||
|
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
||||||
|
},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
rect::Rect,
|
||||||
|
tree::{
|
||||||
|
self, ContainerNode, DisplayNode, FloatNode, Node, NodeVisitor, OutputNode,
|
||||||
|
PlaceholderNode, ToplevelData, ToplevelNodeBase, ToplevelType, WorkspaceNode,
|
||||||
|
},
|
||||||
|
utils::{opaque::OpaqueError, opt::Opt, toplevel_identifier::ToplevelIdentifier},
|
||||||
|
wire::{JayTreeQueryId, jay_tree_query::*},
|
||||||
|
},
|
||||||
|
isnt::std_1::primitive::IsntStrExt,
|
||||||
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
ops::Deref,
|
||||||
|
rc::Rc,
|
||||||
|
str::FromStr,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TREE_TY_DISPLAY: u32 = 1;
|
||||||
|
pub const TREE_TY_OUTPUT: u32 = 2;
|
||||||
|
pub const TREE_TY_WORKSPACE: u32 = 3;
|
||||||
|
pub const TREE_TY_FLOAT: u32 = 4;
|
||||||
|
pub const TREE_TY_CONTAINER: u32 = 5;
|
||||||
|
pub const TREE_TY_PLACEHOLDER: u32 = 6;
|
||||||
|
pub const TREE_TY_XDG_TOPLEVEL: u32 = 7;
|
||||||
|
pub const TREE_TY_X_WINDOW: u32 = 8;
|
||||||
|
pub const TREE_TY_XDG_POPUP: u32 = 9;
|
||||||
|
pub const TREE_TY_LAYER_SURFACE: u32 = 10;
|
||||||
|
pub const TREE_TY_LOCK_SURFACE: u32 = 11;
|
||||||
|
|
||||||
|
pub struct JayTreeQuery {
|
||||||
|
pub id: JayTreeQueryId,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
recursive: Cell<bool>,
|
||||||
|
root: RefCell<Option<Root>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Root {
|
||||||
|
Display,
|
||||||
|
WorkspaceNode(Rc<Opt<WorkspaceNode>>),
|
||||||
|
WorkspaceName(String),
|
||||||
|
ToplevelId(ToplevelIdentifier),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayTreeQuery {
|
||||||
|
pub fn new(client: &Rc<Client>, id: JayTreeQueryId, version: Version) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
recursive: Cell::new(false),
|
||||||
|
root: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_node_position(&self, node: &dyn Node) {
|
||||||
|
let rect = node.node_absolute_position();
|
||||||
|
self.send_position(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_position(&self, rect: Rect) {
|
||||||
|
self.client.event(Position {
|
||||||
|
self_id: self.id,
|
||||||
|
x: rect.x1(),
|
||||||
|
y: rect.y1(),
|
||||||
|
w: rect.width(),
|
||||||
|
h: rect.height(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_not_found(&self) {
|
||||||
|
self.client.event(NotFound { self_id: self.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_done(&self) {
|
||||||
|
self.client.event(Done { self_id: self.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_end(&self) {
|
||||||
|
self.client.event(End { self_id: self.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_start(&self, ty: u32) {
|
||||||
|
self.client.event(Start {
|
||||||
|
self_id: self.id,
|
||||||
|
ty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_client(&self, node: &impl Node) {
|
||||||
|
if let Some(id) = node.node_client_id() {
|
||||||
|
self.client.event(ClientId {
|
||||||
|
self_id: self.id,
|
||||||
|
id: id.raw(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_workspace_name(&self, name: &str) {
|
||||||
|
self.client.event(WorkspaceName {
|
||||||
|
self_id: self.id,
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_output_name(&self, name: &str) {
|
||||||
|
self.client.event(OutputName {
|
||||||
|
self_id: self.id,
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_toplevel(&self, data: &ToplevelData) {
|
||||||
|
self.client.event(Start {
|
||||||
|
self_id: self.id,
|
||||||
|
ty: match &data.kind {
|
||||||
|
ToplevelType::Container => TREE_TY_CONTAINER,
|
||||||
|
ToplevelType::Placeholder(_) => TREE_TY_PLACEHOLDER,
|
||||||
|
ToplevelType::XdgToplevel(_) => TREE_TY_XDG_TOPLEVEL,
|
||||||
|
ToplevelType::XWindow(_) => TREE_TY_X_WINDOW,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
self.client.event(ToplevelId {
|
||||||
|
self_id: self.id,
|
||||||
|
id: &data.identifier.get().to_string(),
|
||||||
|
});
|
||||||
|
self.send_position(data.desired_extents.get());
|
||||||
|
if let Some(cl) = data.client.as_ref().map(|c| c.id.raw()) {
|
||||||
|
self.client.event(ClientId {
|
||||||
|
self_id: self.id,
|
||||||
|
id: cl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.client.event(Title {
|
||||||
|
self_id: self.id,
|
||||||
|
title: &data.title.borrow(),
|
||||||
|
});
|
||||||
|
if let Some(w) = data.workspace.get() {
|
||||||
|
self.send_workspace_name(&w.name);
|
||||||
|
}
|
||||||
|
match &data.kind {
|
||||||
|
ToplevelType::Container => {}
|
||||||
|
ToplevelType::Placeholder(id) => {
|
||||||
|
if let Some(id) = *id {
|
||||||
|
self.client.event(PlaceholderFor {
|
||||||
|
self_id: self.id,
|
||||||
|
id: &id.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToplevelType::XdgToplevel(d) => {
|
||||||
|
self.client.event(AppId {
|
||||||
|
self_id: self.id,
|
||||||
|
app_id: &data.app_id.borrow(),
|
||||||
|
});
|
||||||
|
let tag = &*d.tag.borrow();
|
||||||
|
if tag.is_not_empty() {
|
||||||
|
self.client.event(Tag {
|
||||||
|
self_id: self.id,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToplevelType::XWindow(d) => {
|
||||||
|
if let Some(class) = &*d.info.class.borrow() {
|
||||||
|
self.client.event(XClass {
|
||||||
|
self_id: self.id,
|
||||||
|
class,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(instance) = &*d.info.instance.borrow() {
|
||||||
|
self.client.event(XInstance {
|
||||||
|
self_id: self.id,
|
||||||
|
instance,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(role) = &*d.info.role.borrow() {
|
||||||
|
self.client.event(XRole {
|
||||||
|
self_id: self.id,
|
||||||
|
role,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.is_floating.get() {
|
||||||
|
self.client.event(Floating { self_id: self.id });
|
||||||
|
}
|
||||||
|
if data.visible.get() {
|
||||||
|
self.client.event(Visible { self_id: self.id });
|
||||||
|
}
|
||||||
|
if data.wants_attention.get() {
|
||||||
|
self.client.event(Urgent { self_id: self.id });
|
||||||
|
}
|
||||||
|
for seat_id in data.seat_foci.lock().keys() {
|
||||||
|
for seat in data.state.globals.seats.lock().values() {
|
||||||
|
if seat.id() == *seat_id {
|
||||||
|
self.client.event(Focused {
|
||||||
|
self_id: self.id,
|
||||||
|
global: seat.name().raw(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data.is_fullscreen.get() {
|
||||||
|
self.client.event(Fullscreen { self_id: self.id });
|
||||||
|
}
|
||||||
|
if let Some(ws) = data.workspace.get() {
|
||||||
|
self.client.event(Workspace {
|
||||||
|
self_id: self.id,
|
||||||
|
name: &ws.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayTreeQueryRequestHandler for JayTreeQuery {
|
||||||
|
type Error = JayTreeQueryError;
|
||||||
|
|
||||||
|
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 Some(root) = &*self.root.borrow() else {
|
||||||
|
return Err(JayTreeQueryError::NoRootSet);
|
||||||
|
};
|
||||||
|
match root {
|
||||||
|
Root::Display => Visitor(self).visit_display(&self.client.state.root),
|
||||||
|
Root::WorkspaceNode(n) => match n.get() {
|
||||||
|
Some(n) => Visitor(self).visit_workspace(&n),
|
||||||
|
None => self.send_not_found(),
|
||||||
|
},
|
||||||
|
Root::WorkspaceName(n) => match self.client.state.workspaces.get(n) {
|
||||||
|
Some(n) => Visitor(self).visit_workspace(&n),
|
||||||
|
None => self.send_not_found(),
|
||||||
|
},
|
||||||
|
Root::ToplevelId(id) => match self
|
||||||
|
.client
|
||||||
|
.state
|
||||||
|
.toplevels
|
||||||
|
.get(id)
|
||||||
|
.and_then(|t| t.upgrade())
|
||||||
|
{
|
||||||
|
Some(t) => t.node_visit(&mut Visitor(self)),
|
||||||
|
None => self.send_not_found(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self.send_done();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_root_display(&self, _req: SetRootDisplay, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
*self.root.borrow_mut() = Some(Root::Display);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_recursive(&self, req: SetRecursive, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.recursive.set(req.recursive != 0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_root_workspace(
|
||||||
|
&self,
|
||||||
|
req: SetRootWorkspace,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let ws = self.client.lookup(req.workspace)?;
|
||||||
|
let opt = match ws.workspace.get() {
|
||||||
|
Some(ws) => ws.opt.clone(),
|
||||||
|
_ => Default::default(),
|
||||||
|
};
|
||||||
|
let root = &mut *self.root.borrow_mut();
|
||||||
|
*root = Some(Root::WorkspaceNode(opt));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_root_workspace_name(
|
||||||
|
&self,
|
||||||
|
req: SetRootWorkspaceName,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let root = &mut *self.root.borrow_mut();
|
||||||
|
*root = Some(Root::WorkspaceName(req.workspace.to_owned()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_root_toplevel(&self, req: SetRootToplevel, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let tl = self.client.lookup(req.toplevel)?;
|
||||||
|
let root = &mut *self.root.borrow_mut();
|
||||||
|
*root = Some(Root::ToplevelId(tl.toplevel.tl_data().identifier.get()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_root_window_id(
|
||||||
|
&self,
|
||||||
|
req: SetRootWindowId<'_>,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let id =
|
||||||
|
ToplevelIdentifier::from_str(req.id).map_err(JayTreeQueryError::InvalidToplevelId)?;
|
||||||
|
let root = &mut *self.root.borrow_mut();
|
||||||
|
*root = Some(Root::ToplevelId(id));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Visitor<'a>(&'a JayTreeQuery);
|
||||||
|
|
||||||
|
impl tree::NodeVisitorBase for Visitor<'_> {
|
||||||
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_toplevel(node.tl_data());
|
||||||
|
if s.recursive.get() {
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_toplevel(&mut self, node: &Rc<XdgToplevel>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_toplevel(node.tl_data());
|
||||||
|
node.xdg.for_each_popup(|popup| {
|
||||||
|
NodeVisitor::visit_popup(self, popup);
|
||||||
|
});
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_popup(&mut self, node: &Rc<XdgPopup>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_start(TREE_TY_XDG_POPUP);
|
||||||
|
s.send_node_position(&**node);
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_display(&mut self, node: &Rc<DisplayNode>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_start(TREE_TY_DISPLAY);
|
||||||
|
s.send_node_position(&**node);
|
||||||
|
if s.recursive.get() {
|
||||||
|
for output in node.outputs.lock().values() {
|
||||||
|
NodeVisitor::visit_output(self, output);
|
||||||
|
}
|
||||||
|
for stacked in node.stacked.iter() {
|
||||||
|
if stacked.stacked_has_workspace_link() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stacked.deref().clone().node_visit(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_start(TREE_TY_OUTPUT);
|
||||||
|
s.send_node_position(&**node);
|
||||||
|
s.send_output_name(&node.global.connector.name);
|
||||||
|
if s.recursive.get() {
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_start(TREE_TY_FLOAT);
|
||||||
|
s.send_node_position(&**node);
|
||||||
|
if s.recursive.get() {
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_start(TREE_TY_WORKSPACE);
|
||||||
|
s.send_node_position(&**node);
|
||||||
|
s.send_workspace_name(&node.name);
|
||||||
|
s.send_output_name(&node.output.get().global.connector.name);
|
||||||
|
for stacked in node.stacked.iter() {
|
||||||
|
if stacked.stacked_is_xdg_popup() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stacked.deref().clone().node_visit(self);
|
||||||
|
}
|
||||||
|
if s.recursive.get() {
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_layer_surface(&mut self, node: &Rc<ZwlrLayerSurfaceV1>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_start(TREE_TY_LAYER_SURFACE);
|
||||||
|
s.send_client(&**node);
|
||||||
|
s.send_node_position(&**node);
|
||||||
|
node.for_each_popup(|popup| {
|
||||||
|
NodeVisitor::visit_popup(self, popup);
|
||||||
|
});
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_xwindow(&mut self, node: &Rc<Xwindow>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_toplevel(node.tl_data());
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_toplevel(node.tl_data());
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_lock_surface(&mut self, node: &Rc<ExtSessionLockSurfaceV1>) {
|
||||||
|
let s = self.0;
|
||||||
|
s.send_start(TREE_TY_LOCK_SURFACE);
|
||||||
|
s.send_client(&**node);
|
||||||
|
s.send_node_position(&**node);
|
||||||
|
s.send_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = JayTreeQuery;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for JayTreeQuery {}
|
||||||
|
|
||||||
|
simple_add_obj!(JayTreeQuery);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum JayTreeQueryError {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error("Toplevel id is ill-formed")]
|
||||||
|
InvalidToplevelId(OpaqueError),
|
||||||
|
#[error("No root node was set")]
|
||||||
|
NoRootSet,
|
||||||
|
}
|
||||||
|
efrom!(JayTreeQueryError, ClientError);
|
||||||
|
|
@ -328,6 +328,12 @@ impl XdgSurface {
|
||||||
popup.popup.xdg.set_popup_stack(stack);
|
popup.popup.xdg.set_popup_stack(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_each_popup(&self, mut f: impl FnMut(&Rc<XdgPopup>)) {
|
||||||
|
for popup in self.popups.lock().values() {
|
||||||
|
f(&popup.popup);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XdgSurfaceRequestHandler for XdgSurface {
|
impl XdgSurfaceRequestHandler for XdgSurface {
|
||||||
|
|
|
||||||
|
|
@ -382,6 +382,10 @@ impl StackedNode for XdgPopup {
|
||||||
fn stacked_absolute_position_constrains_input(&self) -> bool {
|
fn stacked_absolute_position_constrains_input(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stacked_is_xdg_popup(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XdgSurfaceExt for XdgPopup {
|
impl XdgSurfaceExt for XdgPopup {
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,12 @@ impl ZwlrLayerSurfaceV1 {
|
||||||
m.layer_surface.get_or_insert_default_ext()
|
m.layer_surface.get_or_insert_default_ext()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_each_popup(&self, mut f: impl FnMut(&Rc<XdgPopup>)) {
|
||||||
|
for popup in self.popups.lock().values() {
|
||||||
|
f(&popup.popup);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
|
impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,10 @@ use {
|
||||||
},
|
},
|
||||||
wheel::{Wheel, WheelError},
|
wheel::{Wheel, WheelError},
|
||||||
wire::{
|
wire::{
|
||||||
JayCompositor, JayCompositorId, JayDamageTracking, JayDamageTrackingId, WlCallbackId,
|
JayCompositor, JayCompositorId, JayDamageTracking, JayDamageTrackingId, JayToplevelId,
|
||||||
WlRegistryId, WlSeatId, jay_compositor, jay_select_toplevel, jay_toplevel, wl_callback,
|
JayWorkspaceId, WlCallbackId, WlRegistryId, WlSeatId, jay_compositor,
|
||||||
wl_display, wl_registry,
|
jay_select_toplevel, jay_select_workspace, jay_toplevel, wl_callback, wl_display,
|
||||||
|
wl_registry,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -362,6 +363,53 @@ impl ToolClient {
|
||||||
Some(id)
|
Some(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn select_workspace(self: &Rc<Self>) -> JayWorkspaceId {
|
||||||
|
let id = self.id();
|
||||||
|
self.send(jay_compositor::SelectWorkspace {
|
||||||
|
self_id: self.jay_compositor().await,
|
||||||
|
id,
|
||||||
|
seat: WlSeatId::NONE,
|
||||||
|
});
|
||||||
|
let ae = Rc::new(AsyncEvent::default());
|
||||||
|
let ws = Rc::new(Cell::new(JayWorkspaceId::NONE));
|
||||||
|
jay_select_workspace::Cancelled::handle(self, id, ae.clone(), |ae, _event| {
|
||||||
|
ae.trigger();
|
||||||
|
});
|
||||||
|
jay_select_workspace::Selected::handle(
|
||||||
|
self,
|
||||||
|
id,
|
||||||
|
(ae.clone(), ws.clone()),
|
||||||
|
|(ae, ws), event| {
|
||||||
|
ws.set(event.id);
|
||||||
|
ae.trigger();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ae.triggered().await;
|
||||||
|
ws.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn select_toplevel(self: &Rc<Self>) -> JayToplevelId {
|
||||||
|
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 toplevel = Rc::new(Cell::new(JayToplevelId::NONE));
|
||||||
|
jay_select_toplevel::Done::handle(
|
||||||
|
self,
|
||||||
|
id,
|
||||||
|
(ae.clone(), toplevel.clone()),
|
||||||
|
|(ae, toplevel), event| {
|
||||||
|
toplevel.set(event.id);
|
||||||
|
ae.trigger();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ae.triggered().await;
|
||||||
|
toplevel.get()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn select_toplevel_client(self: &Rc<Self>) -> u64 {
|
pub async fn select_toplevel_client(self: &Rc<Self>) -> u64 {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
self.send(jay_compositor::SelectToplevel {
|
self.send(jay_compositor::SelectToplevel {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ impl PlaceholderNode {
|
||||||
state,
|
state,
|
||||||
node.tl_data().title.borrow().clone(),
|
node.tl_data().title.borrow().clone(),
|
||||||
node.node_client(),
|
node.node_client(),
|
||||||
ToplevelType::Placeholder,
|
ToplevelType::Placeholder(Some(node.tl_data().identifier.get())),
|
||||||
id,
|
id,
|
||||||
slf,
|
slf,
|
||||||
),
|
),
|
||||||
|
|
@ -75,7 +75,7 @@ impl PlaceholderNode {
|
||||||
state,
|
state,
|
||||||
String::new(),
|
String::new(),
|
||||||
None,
|
None,
|
||||||
ToplevelType::Placeholder,
|
ToplevelType::Placeholder(None),
|
||||||
id,
|
id,
|
||||||
slf,
|
slf,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ pub trait StackedNode: Node {
|
||||||
fn stacked_absolute_position_constrains_input(&self) -> bool {
|
fn stacked_absolute_position_constrains_input(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stacked_is_xdg_popup(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PinnedNode: StackedNode {
|
pub trait PinnedNode: StackedNode {
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,7 @@ impl ToplevelOpt {
|
||||||
|
|
||||||
pub enum ToplevelType {
|
pub enum ToplevelType {
|
||||||
Container,
|
Container,
|
||||||
Placeholder,
|
Placeholder(Option<ToplevelIdentifier>),
|
||||||
XdgToplevel(Rc<XdgToplevelToplevelData>),
|
XdgToplevel(Rc<XdgToplevelToplevelData>),
|
||||||
XWindow(Rc<XwindowData>),
|
XWindow(Rc<XwindowData>),
|
||||||
}
|
}
|
||||||
|
|
@ -288,7 +288,7 @@ impl ToplevelType {
|
||||||
pub fn to_window_type(&self) -> WindowType {
|
pub fn to_window_type(&self) -> WindowType {
|
||||||
match self {
|
match self {
|
||||||
ToplevelType::Container => window::CONTAINER,
|
ToplevelType::Container => window::CONTAINER,
|
||||||
ToplevelType::Placeholder => window::PLACEHOLDER,
|
ToplevelType::Placeholder { .. } => window::PLACEHOLDER,
|
||||||
ToplevelType::XdgToplevel { .. } => window::XDG_TOPLEVEL,
|
ToplevelType::XdgToplevel { .. } => window::XDG_TOPLEVEL,
|
||||||
ToplevelType::XWindow { .. } => window::X_WINDOW,
|
ToplevelType::XWindow { .. } => window::X_WINDOW,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,10 @@ request kill_client (since = 18) {
|
||||||
id: pod(u64),
|
id: pod(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request create_tree_query (since = 18) {
|
||||||
|
id: id(jay_tree_query),
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event client_id {
|
event client_id {
|
||||||
|
|
|
||||||
102
wire/jay_tree_query.txt
Normal file
102
wire/jay_tree_query.txt
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
request destroy { }
|
||||||
|
|
||||||
|
request execute { }
|
||||||
|
|
||||||
|
request set_root_display { }
|
||||||
|
|
||||||
|
request set_recursive {
|
||||||
|
recursive: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request set_root_workspace {
|
||||||
|
workspace: id(jay_workspace),
|
||||||
|
}
|
||||||
|
|
||||||
|
request set_root_workspace_name {
|
||||||
|
workspace: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
request set_root_toplevel {
|
||||||
|
toplevel: id(jay_toplevel),
|
||||||
|
}
|
||||||
|
|
||||||
|
request set_root_window_id {
|
||||||
|
id: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event done { }
|
||||||
|
|
||||||
|
event not_found { }
|
||||||
|
|
||||||
|
event start {
|
||||||
|
ty: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event end { }
|
||||||
|
|
||||||
|
event position {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
w: i32,
|
||||||
|
h: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event workspace_name {
|
||||||
|
name: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event output_name {
|
||||||
|
name: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event toplevel_id {
|
||||||
|
id: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event client_id {
|
||||||
|
id: pod(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
event title {
|
||||||
|
title: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event app_id {
|
||||||
|
app_id: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event floating { }
|
||||||
|
|
||||||
|
event visible { }
|
||||||
|
|
||||||
|
event urgent { }
|
||||||
|
|
||||||
|
event focused {
|
||||||
|
global: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event fullscreen { }
|
||||||
|
|
||||||
|
event tag {
|
||||||
|
tag: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event x_class {
|
||||||
|
class: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event x_instance {
|
||||||
|
instance: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event x_role {
|
||||||
|
role: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event workspace {
|
||||||
|
name: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
event placeholder_for {
|
||||||
|
id: str,
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue