Add display tiles, three output formats, and hjkl
Displays are capture sources too — ext-image-capture-source-v1 makes one
from a wl_output just as it does from a toplevel handle — so they are now
tiles as well, appended after the windows and labelled "NAME · display".
They are snapshot-only: a live display tile contains this overlay, which
contains the display tile, and refreshing that never settles while
costing a whole screen per frame. They also only get one buffer for the
same reason, which is worth ~29MB here.
The bigger change is what wlgrid reports. It was focusing the pick itself
and printing only under --print, which suits a keybinding and nothing
else. Now it is a chooser: it always reports the pick, never acts unless
asked (--focus), exits 1 when cancelled, and can say it three ways.
--format portal emits what xdg-desktop-portal-wlr's simple chooser reads
("Monitor: NAME" / "Window: <foreign-toplevel identifier>"), so wlgrid
can be the picker behind getDisplayMedia, with live previews of windows
and displays. That contract is also why tsv carries both identifiers: the
portal and grim -T want the toplevel identifier, sway scripting wants the
con_id. --format json gives the whole record for jq.
Navigation also takes hjkl, and --help now describes every flag with its
default plus the output formats.
This commit is contained in:
+30
-34
@@ -5,38 +5,17 @@
|
||||
|
||||
use swayipc::{Connection, Node, NodeType};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Win {
|
||||
pub con_id: i64,
|
||||
pub app: String,
|
||||
pub title: String,
|
||||
/// ext-foreign-toplevel-list-v1 identifier; the key we match capture
|
||||
/// sources on.
|
||||
pub ft_id: String,
|
||||
}
|
||||
use crate::target::Target;
|
||||
|
||||
impl Win {
|
||||
/// "title · app", the label rofigrid was given (app dropped when empty).
|
||||
/// Used once tiles are labelled.
|
||||
#[allow(dead_code)]
|
||||
pub fn label(&self) -> String {
|
||||
if self.app.is_empty() {
|
||||
self.title.clone()
|
||||
} else {
|
||||
format!("{} · {}", self.title, self.app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Every view in the tree, in tree order (same traversal the jq filter did, so
|
||||
/// the grid keeps the ordering the muscle memory expects).
|
||||
pub fn windows(conn: &mut Connection) -> Result<Vec<Win>, swayipc::Error> {
|
||||
/// Every view in the tree, in tree order (the same traversal the jq filter did,
|
||||
/// so the grid keeps the ordering the muscle memory expects).
|
||||
pub fn windows(conn: &mut Connection) -> Result<Vec<Target>, swayipc::Error> {
|
||||
let mut out = Vec::new();
|
||||
collect(&conn.get_tree()?, &mut out);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn collect(node: &Node, out: &mut Vec<Win>) {
|
||||
fn collect(node: &Node, out: &mut Vec<Target>) {
|
||||
let is_con = matches!(node.node_type, NodeType::Con | NodeType::FloatingCon);
|
||||
let class = node
|
||||
.window_properties
|
||||
@@ -45,20 +24,37 @@ fn collect(node: &Node, out: &mut Vec<Win>) {
|
||||
if is_con && (node.app_id.is_some() || class.is_some()) {
|
||||
// A view with no identifier can't be captured, but it still belongs in
|
||||
// the list: it gets a tile with no thumbnail.
|
||||
out.push(Win {
|
||||
con_id: node.id,
|
||||
app: node.app_id.clone().or(class).unwrap_or_default(),
|
||||
title: node.name.clone().unwrap_or_default(),
|
||||
ft_id: node.foreign_toplevel_identifier.clone().unwrap_or_default(),
|
||||
});
|
||||
out.push(Target::window(
|
||||
node.id,
|
||||
node.foreign_toplevel_identifier.clone().unwrap_or_default(),
|
||||
node.app_id.clone().or(class).unwrap_or_default(),
|
||||
node.name.clone().unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
for child in node.nodes.iter().chain(node.floating_nodes.iter()) {
|
||||
collect(child, out);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus(conn: &mut Connection, con_id: i64) -> Result<(), swayipc::Error> {
|
||||
for res in conn.run_command(format!("[con_id={con_id}] focus"))? {
|
||||
/// The largest integer scale in use, which is what the overlay renders at.
|
||||
pub fn scale(conn: &mut Connection) -> Result<i32, swayipc::Error> {
|
||||
Ok(conn
|
||||
.get_outputs()?
|
||||
.iter()
|
||||
.filter(|o| o.active)
|
||||
.map(|o| o.scale.unwrap_or(1.0).ceil() as i32)
|
||||
.max()
|
||||
.unwrap_or(1)
|
||||
.max(1))
|
||||
}
|
||||
|
||||
pub fn focus(conn: &mut Connection, target: &Target) -> Result<(), swayipc::Error> {
|
||||
let cmd = match target.con_id {
|
||||
Some(con_id) => format!("[con_id={con_id}] focus"),
|
||||
// Picking a display means going to it.
|
||||
None => format!("focus output {}", target.id),
|
||||
};
|
||||
for res in conn.run_command(cmd)? {
|
||||
res?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user