use std::{
env, fs,
io::{self, BufRead, Read},
os::unix::net::UnixStream,
path::PathBuf,
process::Command,
thread,
time::Duration,
};
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
print_help();
return;
}
let mut dock = HyprDock {
monitor_name: parse_monitor_name(),
bar: None,
};
let mut iter = args.iter();
iter.next();
let mut iteration = 0;
for _ in 0..args.len() - 1 {
if iteration == args.len() - 1 {
break;
}
iteration += 1;
match iter.next().unwrap().as_str() {
"--internal" | "-i" => dock.internal_monitor(),
"--external" | "-e" => dock.external_monitor(),
"--extend" | "-eo" => dock.extend_monitor(),
"--mirror" | "-io" => dock.mirror_monitor(),
"--server" | "-s" => dock.socket_connect(),
"--version" | "-v" => println!("0.2.0"),
"--bar" | "-b" => {
dock.bar = {
iteration += 1;
Some(iter.next().unwrap().clone())
}
}
"--help" | "-h" => {
print_help();
return;
}
x => {
println!("Could not parse {}", x);
print_help();
return;
}
}
}
}
fn print_help() {
print!(
"Possible arguments are:
--extend/-e: Extends monitors
--mirror/-m: Mirrors monitors
--internal/-io: Switch to internal monitor only
--external/-eo: Switch to external monitor only
--server/-s: daemon version
automatically handles actions on laptop lid close and open.
--bar/-b: selects a bar to start when monitor switches (used for eww)
--help/-h: shows options
--version/-v: shows version\n"
);
}
fn parse_monitor_name() -> String {
let path = home::home_dir()
.unwrap()
.join(PathBuf::from(".config/hypr/hyprland.conf"));
let file = fs::File::open(path.to_str().unwrap())
.expect("Could not open hyprland config, make sure it exists.");
for line in io::BufReader::new(file).lines() {
if line.as_ref().unwrap().contains("monitor") {
let mut name = String::new();
let mut add_to_name = false;
for char in line.unwrap().chars() {
if char == '=' {
add_to_name = true;
} else if char == ',' {
return name;
} else if add_to_name {
name.push(char);
}
}
}
}
panic!("Could not read name for monitor!");
}
struct HyprDock {
pub monitor_name: String,
pub bar: Option<String>,
}
impl HyprDock {
pub fn handle_close(&self) {
if self.has_external_monitor() {
self.external_monitor();
thread::sleep(Duration::from_millis(1000));
self.restart_hyprpaper();
self.restart_eww_bar();
} else {
self.stop_music();
self.lock_system();
}
}
pub fn handle_open(&self) {
if self.is_internal_active() {
return;
}
if !self.has_external_monitor() {
self.internal_monitor();
self.restart_hyprpaper();
self.restart_eww_bar();
self.fix_eww_bar();
return;
} else {
self.internal_monitor();
self.extend_monitor();
self.restart_hyprpaper();
self.restart_eww_bar();
self.fix_eww_bar();
}
}
pub fn handle_event(&self, event: &str) {
match event {
"button/lid LID close\n" => self.handle_close(),
"button/lid LID open\n" => self.handle_open(),
_ => {}
}
}
pub fn socket_connect(&self) {
let mut sock =
UnixStream::connect("/var/run/acpid.socket").expect("failed to connect to socket");
loop {
let mut buf = [0; 1024];
let n = sock.read(&mut buf).expect("failed to read from socket");
let data = std::str::from_utf8(&buf[..n]).unwrap().to_string();
self.handle_event(data.as_str());
}
}
pub fn lock_system(&self) {
Command::new("swaylock")
.arg("-c")
.arg("000000")
.spawn()
.expect("Failed to lock screen");
Command::new("systemctl")
.arg("suspend")
.output()
.expect("Failed to suspend");
}
pub fn stop_music(&self) {
Command::new("playerctl")
.arg("--all-players")
.arg("-a")
.arg("pause")
.output()
.expect("Failed to pause music players");
}
pub fn extend_monitor(&self) {
if !self.is_internal_active() {
self.restart_internal();
}
Command::new("hyprctl")
.arg("keyword")
.arg("monitor")
.arg(",highrr,auto,1")
.output()
.expect("Failed to extend Monitors");
}
pub fn mirror_monitor(&self) {
if !self.is_internal_active() {
self.restart_internal();
}
Command::new("hyprctl")
.arg("keyword")
.arg("monitor")
.arg(",highrr,auto,1,mirror,".to_string() + self.monitor_name.as_str())
.output()
.expect("Failed to mirror Monitors");
}
pub fn internal_monitor(&self) {
let needs_restart = !self.is_internal_active();
Command::new("hyprctl")
.arg("keyword")
.arg("monitor")
.arg(self.monitor_name.clone() + ",highrr,0x0,1")
.output()
.expect("Failed to enable internal monitor");
Command::new("hyprctl")
.arg("keyword")
.arg("monitor")
.arg(",disabled")
.output()
.expect("Failed to disable external monitor");
if needs_restart {
self.restart_eww_bar();
self.restart_hyprpaper();
}
}
pub fn restart_internal(&self) {
Command::new("hyprctl")
.arg("keyword")
.arg("monitor")
.arg(self.monitor_name.clone() + ",highrr,0x0,1")
.output()
.expect("Failed to enable internal monitor");
self.restart_hyprpaper();
self.restart_eww_bar();
self.fix_eww_bar();
}
pub fn external_monitor(&self) {
let needs_restart = !self.is_internal_active();
Command::new("hyprctl")
.arg("keyword")
.arg("monitor")
.arg(",highrr,0x0,1")
.output()
.expect("Failed to enable external monitor");
Command::new("hyprctl")
.arg("keyword")
.arg("monitor")
.arg("eDP-1,disabled")
.output()
.expect("Failed to disable internal monitor");
if needs_restart {
self.restart_eww_bar();
self.restart_hyprpaper();
}
}
pub fn restart_hyprpaper(&self) {
Command::new("hyprctl")
.arg("dispatch")
.arg("exec")
.arg("hyprpaper")
.output()
.expect("Could not restart hyprpaper");
}
pub fn restart_eww_bar(&self) {
if !self.bar.is_some() {
return;
}
let bar = self.bar.as_ref().clone().unwrap();
Command::new(bar)
.arg("close-all")
.output()
.expect("could not close eww windows");
Command::new(bar)
.arg("open")
.arg("bar")
.output()
.expect("Could not open eww bar");
}
pub fn fix_eww_bar(&self) {
if !self.bar.is_some() {
return;
}
let bar = self.bar.as_ref().clone().unwrap();
Command::new(bar).arg("reload").output().expect("pingpang");
}
pub fn is_internal_active(&self) -> bool {
let output = String::from_utf8(
Command::new("hyprctl")
.arg("monitors")
.output()
.expect("Failed to use only external monitor")
.stdout,
)
.unwrap();
if output.contains(self.monitor_name.as_str()) {
return true;
}
false
}
pub fn has_external_monitor(&self) -> bool {
let output = String::from_utf8(
Command::new("hyprctl")
.arg("monitors")
.output()
.expect("Failed to use only external monitor")
.stdout,
)
.unwrap();
if output.contains("ID 1") {
return true;
}
false
}
}