use std::{
collections::hash_map::DefaultHasher,
fs::File,
hash::{Hash, Hasher},
io::Write,
path::PathBuf,
process::Command,
};
use serde::{Deserialize, Serialize};
use super::Monitor;
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct HyprMonitor {
id: i64,
name: String,
description: String,
make: String,
model: String,
serial: String,
width: i64,
height: i64,
refreshRate: f64,
x: i64,
y: i64,
scale: f64,
transform: i64,
vrr: bool,
}
impl Hash for HyprMonitor {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.description.hash(state);
self.make.hash(state);
self.model.hash(state);
self.serial.hash(state);
}
}
impl HyprMonitor {
pub fn convert_data(&self) -> Monitor {
Monitor {
name: self.name.clone(),
make: self.make.clone(),
model: self.model.clone(),
serial: self.serial.clone(),
resolution: self.width.to_string() + "x" + &self.height.to_string(),
refreshrate: (self.refreshRate as i64).to_string(),
offset: self.x.to_string() + "x" + &self.y.to_string(),
scale: self.scale.to_string(),
transform: self.transform.to_string(),
vrr: self.vrr,
}
}
}
pub fn get_hypr_monitor_info() -> Vec<u8> {
Command::new("hyprctl")
.args(["-j", "monitors"])
.output()
.expect("Could not save output to file")
.stdout
}
pub fn get_current_monitor_hash(name: Option<&String>) -> String {
let monitors: Vec<HyprMonitor> = serde_json::from_str(
&String::from_utf8(get_hypr_monitor_info()).expect("Could not parse json"),
)
.expect("Could not parse json");
let mut s = DefaultHasher::new();
for monitor in monitors.iter() {
monitor.hash(&mut s);
}
name.hash(&mut s);
s.finish().to_string()
}
pub fn save_hypr_monitor_data(path: String, name: Option<&String>, hash: Option<&String>) {
let monitor_name = get_current_monitor_hash(name);
let monitor_hash = hash.unwrap_or(&monitor_name);
let mut file = File::create(path + "monitor_configs/" + &monitor_hash + ".json")
.expect("Could not open json file");
file.write_all(&get_hypr_monitor_info())
.expect("Could not write to file");
}
pub fn try_get_monitor_hash_path(base_path: String, hash: &String) -> Option<PathBuf> {
let path = PathBuf::from(base_path + "/monitor_configs" + &hash + ".json");
if path.is_file() { Some(path) } else { None }
}
pub fn import_hypr_data(
base_path: String,
name: Option<&String>,
hash: Option<&String>,
) -> Option<Vec<Monitor>> {
use std::io::prelude::*;
let monitor_name = &get_current_monitor_hash(name);
let monitor_hash = hash.unwrap_or(monitor_name);
let path = try_get_monitor_hash_path(base_path, monitor_hash)?;
let mut file = File::open(path).ok()?;
let mut contents = String::new();
file.read_to_string(&mut contents).ok()?;
let hyprmonitors: Vec<HyprMonitor> = serde_json::from_str(contents.as_str()).ok()?;
let mut monitors = Vec::new();
for monitor in hyprmonitors {
monitors.push(monitor.convert_data());
}
Some(monitors)
}
pub fn set_hypr_monitors_from_hyprvec(monitors: Vec<HyprMonitor>) {
for monitor in monitors {
let new_monitor = monitor.convert_data();
new_monitor.enable_hypr_monitor();
}
}
pub fn set_hypr_monitors_from_file(path: String, name: Option<&String>, hash: Option<&String>) {
let monitors_opt = import_hypr_data(path, name, hash);
if let Some(monitors) = monitors_opt {
for monitor in monitors {
monitor.enable_hypr_monitor();
}
}
}
#[test]
fn import_data_test() {
use std::fs::File;
use std::io::prelude::*;
let file = File::open("example.json");
if file.is_err() {
return;
}
let mut file = file.unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents)
.expect("Could not read data from file");
let p: Vec<HyprMonitor> =
serde_json::from_str(contents.as_str()).expect("Could not parse json");
dbg!(p);
}