97 lines
2.4 KiB
Rust
97 lines
2.4 KiB
Rust
use clap::{Parser, ValueEnum};
|
|
use swayipc::{Connection, Node};
|
|
use std::fmt;
|
|
|
|
/// sway program focuser/unfocuser/launcher
|
|
///
|
|
/// Launches a program if it isn't running.
|
|
/// If it is running, but not focused, focuses it.
|
|
/// If it is running and focused, sends it to the scratchpad.
|
|
#[derive(Parser)]
|
|
#[command(version)]
|
|
struct Cli {
|
|
/// Qualifier to use
|
|
#[arg(short, long, default_value_t = UseField::Class)]
|
|
use_field: UseField,
|
|
|
|
/// The window's identifier as per the qualifier
|
|
#[arg(short, long)]
|
|
identifier: String,
|
|
|
|
/// Command to use to launch the application
|
|
#[arg(short, long)]
|
|
cmd: String,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
|
enum UseField {
|
|
Class,
|
|
AppId,
|
|
}
|
|
|
|
impl fmt::Display for UseField {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "{}",
|
|
match self {
|
|
UseField::Class => "class",
|
|
UseField::AppId => "app_id",
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
fn find_node<F>(root: Node, predicate: F) -> Option<Node>
|
|
where
|
|
F: Fn(&Node) -> bool,
|
|
{
|
|
let mut stack = vec![root];
|
|
|
|
while let Some(node) = stack.pop() {
|
|
if predicate(&node) {
|
|
return Some(node);
|
|
}
|
|
|
|
stack.extend(node.floating_nodes.into_iter());
|
|
stack.extend(node.nodes.into_iter());
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>>{
|
|
let cli = Cli::parse();
|
|
let mut connection = Connection::new()?;
|
|
let tree = connection.get_tree()?;
|
|
|
|
let node = match cli.use_field {
|
|
UseField::Class => find_node(tree, |node: &Node| {
|
|
node.window_properties
|
|
.as_ref()
|
|
.and_then(|p| p.class.as_deref())
|
|
== Some(&cli.identifier)
|
|
}),
|
|
UseField::AppId => find_node(tree, |node: &Node| {
|
|
node.app_id.as_deref() == Some(&cli.identifier)
|
|
}),
|
|
};
|
|
|
|
let sway_cmd = if let Some(node) = node {
|
|
if node.focused {
|
|
format!("[{}={}] move scratchpad", cli.use_field, cli.identifier)
|
|
} else {
|
|
format!("[{}={}] focus", cli.use_field, cli.identifier)
|
|
}
|
|
} else {
|
|
format!("exec {}", cli.cmd)
|
|
};
|
|
|
|
let result = connection.run_command(&sway_cmd)?;
|
|
|
|
if let Some(res) = result.first() {
|
|
if res.is_err() {
|
|
return Err(format!("command failed: {:?}", res).into());
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|