176 lines
4.8 KiB
Rust
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!()
|
|
}
|