nanpa/src/bin/nanpad.rs

176 lines
4.8 KiB
Rust

#[macro_use]
extern crate log;
use dbus::channel::MatchingReceiver;
use dbus::message::MatchRule;
use dbus_crossroads::Crossroads;
use dbus_tokio::connection;
use futures::future;
use serde::Deserialize;
use std::{collections::HashMap, io};
use tokio_i3ipc::I3;
#[derive(Debug, Clone)]
struct Nanpa {
config: Config,
}
async fn get_active_output_name() -> io::Result<String> {
let mut sock = I3::connect().await?;
for ws in sock.get_workspaces().await? {
debug!("ws: {:?}", ws);
if ws.focused {
return Ok(ws.output);
}
}
Err(io::Error::new(
io::ErrorKind::Other,
"can't get active output somehow",
))
}
#[derive(Deserialize, Debug, Clone)]
struct Config {
outputs: HashMap<String, u32>,
}
fn make_workspace_name(num: u32) -> String {
match num % 10 {
1 => format!("{}:", num),
2 => format!("{}:", num),
3 => format!("{}:", num),
4 => format!("{}:", num),
5 => format!("{}:", num),
6 => format!("{}:", num),
7 => format!("{}:", num),
8 => format!("{}:", num),
9 => format!("{}:", num),
0 => format!("{}:", num),
_ => panic!("this should be impossible"),
}
}
#[test]
fn test_workspace_name() {
for i in 1..99 {
println!("{}: {}", i, make_workspace_name(i));
}
}
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
debug!("connecting to dbus");
let (resource, c) = connection::new_session_sync()?;
tokio::spawn(async {
let err = resource.await;
panic!("Lost connection to D-Bus: {}", err);
});
c.request_name("website.christine.nanpa", false, true, false)
.await?;
let mut cr = Crossroads::new();
cr.set_async_support(Some((
c.clone(),
Box::new(|x| {
tokio::spawn(x);
}),
)));
let iface_token = cr.register("website.christine.nanpa", |b| {
b.method_with_cr_async(
"Move",
("number",),
("reply",),
|mut ctx, cr, (number,): (u32,)| {
let nanpa: &mut Nanpa = cr.data_mut(ctx.path()).unwrap();
let cfg = nanpa.config.clone();
async move {
let output = get_active_output_name().await.unwrap();
let number = cfg.outputs[&output] + number;
let ws_name = make_workspace_name(number);
debug!(
"output: {}, number: {}, ws_name: {:?}",
output, number, ws_name
);
let mut sock = I3::connect().await.unwrap();
sock.run_command(format!("move container to workspace {}", ws_name))
.await
.unwrap();
ctx.reply(Ok(("OK".to_string(),)))
}
},
);
b.method_with_cr_async(
"Switch",
("number",),
("reply",),
|mut ctx, cr, (number,): (u32,)| {
let nanpa: &mut Nanpa = cr.data_mut(ctx.path()).unwrap();
let cfg = nanpa.config.clone();
async move {
let output = get_active_output_name().await.unwrap();
let number = cfg.outputs[&output] + number;
let ws_name = make_workspace_name(number);
debug!(
"output: {}, number: {}, ws_name: {:?}",
output, number, ws_name
);
let mut sock = I3::connect().await.unwrap();
sock.run_command(format!("workspace {}", ws_name))
.await
.unwrap();
ctx.reply(Ok(("OK".to_string(),)))
}
},
);
});
let mut display_bases: HashMap<String, u32> = HashMap::new();
let mut sock = I3::connect().await?;
for (i, output) in sock.get_outputs().await?.into_iter().enumerate() {
let offset: u32 = i as u32 * 10;
sock.run_command(format!("focus output {}", output.name))
.await?;
display_bases.insert(output.name, offset);
let ws_name = make_workspace_name(offset + 1);
sock.run_command(format!("workspace {}", ws_name)).await?;
}
debug!("{:?}", display_bases);
let nanpa = Nanpa {
config: Config {
outputs: display_bases,
},
};
cr.insert("/", &[iface_token], nanpa.clone());
c.start_receive(
MatchRule::new_method_call(),
Box::new(move |msg, conn| {
cr.handle_message(msg, conn).unwrap();
true
}),
);
future::pending::<()>().await;
unreachable!()
}