rust
This commit is contained in:
parent
e2e5e2162c
commit
8694a020b0
9 changed files with 1452 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
bin/
|
||||
result
|
||||
|
||||
|
||||
# Added by cargo
|
||||
|
||||
/target
|
1159
Cargo.lock
generated
Normal file
1159
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "hyprman"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.4", features = ["derive"] }
|
||||
homedir = "0.2.1"
|
||||
hyprland = "0.3.13"
|
||||
serde = "1.0.197"
|
||||
serde_json = "1.0.115"
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Daylin Morgan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# hyprman
|
||||
|
||||
Utility to output the workspace state to be ingested by `eww`.
|
27
flake.lock
Normal file
27
flake.lock
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1706006310,
|
||||
"narHash": "sha256-nDPz0fj0IFcDhSTlXBU2aixcnGs2Jm4Zcuoj0QtmiXQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b43bb235efeab5324c5e486882ef46749188eee2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
34
flake.nix
Normal file
34
flake.nix
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
description = "hyprman";
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
};
|
||||
|
||||
outputs = inputs @ {
|
||||
self,
|
||||
nixpkgs,
|
||||
}: let
|
||||
inherit (nixpkgs.lib) genAttrs;
|
||||
supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
|
||||
forAllSystems = f: genAttrs supportedSystems (system: f nixpkgs.legacyPackages.${system});
|
||||
in {
|
||||
devShells = forAllSystems (pkgs: {
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
nim
|
||||
];
|
||||
};
|
||||
});
|
||||
packages = forAllSystems (
|
||||
pkgs: {
|
||||
hyprman = pkgs.rustPlatform.buildRustPackage {
|
||||
pname = "hyprman";
|
||||
version = "2023.1001";
|
||||
src = ./.;
|
||||
cargoLock.lockFile = ./Cargo.lock;
|
||||
};
|
||||
default = self.packages.${pkgs.system}.hyprman;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
183
src/main.rs
Normal file
183
src/main.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use clap::{Parser, Subcommand};
|
||||
use hyprland::{
|
||||
data::{Clients, Monitors},
|
||||
event_listener::EventListenerMutable as EventListener,
|
||||
prelude::*,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
fs::File,
|
||||
io::{self, Read},
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
thread, time,
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(version, about)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Option<Commands>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Commands {
|
||||
/// watch workspace/client activity and write to stdout
|
||||
Workspace {
|
||||
/// path to icons.json
|
||||
#[arg(short, long)]
|
||||
icons: Option<PathBuf>,
|
||||
},
|
||||
/// reflect monitor changes to eww
|
||||
Monitors {},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
struct EwwData {
|
||||
id: usize,
|
||||
icon: String,
|
||||
class: String,
|
||||
}
|
||||
|
||||
fn load_icons(path: &Option<PathBuf>) -> io::Result<HashMap<String, String>> {
|
||||
|
||||
let icon_path: PathBuf = match path {
|
||||
Some(path) => PathBuf::from(path),
|
||||
None => {
|
||||
(env::var("XDG_CONFIG_DIR").map_or(
|
||||
homedir::get_my_home().unwrap().unwrap().join(".config"),
|
||||
PathBuf::from,
|
||||
))
|
||||
.join("hyprman")
|
||||
.join("icons.json")
|
||||
}
|
||||
};
|
||||
|
||||
let mut file = File::open(icon_path)?;
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data)?;
|
||||
let icons: HashMap<String, String> = serde_json::from_str(&data)?;
|
||||
Ok(icons)
|
||||
}
|
||||
|
||||
struct Monitors {}
|
||||
|
||||
fn get_state(icons: &HashMap<String, String>) -> hyprland::Result<()> {
|
||||
let monitors = Monitors::get()?;
|
||||
let clients = Clients::get()?;
|
||||
|
||||
let mut state: Vec<Vec<EwwData>> = vec![];
|
||||
let mut workspaces: Vec<EwwData> = (1..10)
|
||||
.map(|i| EwwData {
|
||||
id: i,
|
||||
icon: "".to_string(),
|
||||
class: format!("ws-button-{}", i - 1),
|
||||
})
|
||||
.collect();
|
||||
|
||||
for client in clients {
|
||||
let id = client.workspace.id;
|
||||
if id < 0 {
|
||||
continue;
|
||||
}
|
||||
match icons.get(&client.class) {
|
||||
Some(icon) => workspaces[(id - 1) as usize].icon.push_str(icon),
|
||||
None => workspaces[(id - 1) as usize].icon.push(''),
|
||||
}
|
||||
}
|
||||
for workspace in &mut workspaces {
|
||||
if workspace.icon.is_empty() {
|
||||
workspace.icon = "".to_string();
|
||||
}
|
||||
}
|
||||
for monitor in monitors {
|
||||
let mut workspaces = workspaces.clone();
|
||||
workspaces[(monitor.active_workspace.id - 1) as usize]
|
||||
.class
|
||||
.push_str(" ws-button-open");
|
||||
state.push(workspaces);
|
||||
}
|
||||
println!("{}", serde_json::to_string(&state)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn watch_workspaces(icons: &Option<PathBuf>) -> hyprland::Result<()> {
|
||||
let icons = load_icons(icons)?;
|
||||
loop {
|
||||
let delay = time::Duration::from_millis(500);
|
||||
thread::sleep(delay);
|
||||
get_state(&icons)?;
|
||||
}
|
||||
}
|
||||
|
||||
struct Eww {}
|
||||
|
||||
enum EwwCommand {
|
||||
Open,
|
||||
Close,
|
||||
}
|
||||
|
||||
impl Eww {
|
||||
fn call(cmd: EwwCommand, bar: &str) -> io::Result<()> {
|
||||
// give the window time to react
|
||||
|
||||
Command::new("eww")
|
||||
.arg(match cmd {
|
||||
EwwCommand::Open => "open",
|
||||
EwwCommand::Close => "close",
|
||||
})
|
||||
.arg("bar".to_string() + bar)
|
||||
.status()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init() -> hyprland::Result<()> {
|
||||
let monitors = Monitors::get()?;
|
||||
for (i, _) in monitors.enumerate() {
|
||||
Eww::call(EwwCommand::Open, &format!("{}", i))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delay() {
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
}
|
||||
|
||||
fn open(bar: &str) -> io::Result<()> {
|
||||
Eww::delay();
|
||||
Eww::call(EwwCommand::Open, bar)?;
|
||||
Ok(())
|
||||
}
|
||||
fn close(bar: &str) -> io::Result<()> {
|
||||
Eww::delay();
|
||||
Eww::call(EwwCommand::Close, bar)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn listen_for_monitors() -> hyprland::Result<()> {
|
||||
Eww::init()?;
|
||||
|
||||
let mut event_listener = EventListener::new();
|
||||
event_listener.add_monitor_added_handler(|_, _| Eww::open("1").expect("failed to open bar"));
|
||||
event_listener.add_monitor_removed_handler(|_, _| Eww::close("1").expect("failed to open bar"));
|
||||
event_listener.start_listener()
|
||||
}
|
||||
|
||||
fn main() -> hyprland::Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
if let Some(cmd) = &cli.command {
|
||||
match cmd {
|
||||
Commands::Workspace { icons } => watch_workspaces(icons)?,
|
||||
Commands::Monitors {} => listen_for_monitors()?,
|
||||
}
|
||||
} else {
|
||||
println!("no command specified see: hyprman help")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
5
todo.md
Normal file
5
todo.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# hyprman todo's
|
||||
|
||||
- [ ] implement the eww infra to listen and close/open bar's on socket2
|
||||
|
||||
<!-- generated with <3 by daylinmorgan/todo -->
|
Loading…
Reference in a new issue