parent
7df555a756
commit
07c9950cb8
8 changed files with 353 additions and 23 deletions
|
@ -1,34 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# TODO: we're mixing bash arrays and not-arrays - get it together
|
||||
declare -A OCCUPIED
|
||||
declare -A ACTIVE
|
||||
declare -A FOCUSED
|
||||
|
||||
#define icons for workspaces 1-9
|
||||
ic=(1 2 3 4 5 6 7 8 9)
|
||||
spaces=(1 2 3 4 5 6 7 8 9)
|
||||
icons=(1 2 3 4 5 6 7 8 9)
|
||||
|
||||
occ() { export o"$1"="occupied"; }
|
||||
unocc() { unset -v o"$1"; }
|
||||
occupy() { export OCCUPIED["$1"]=occupied; }
|
||||
unoccupy() { unset "OCCUPIED[$1]"; }
|
||||
|
||||
active() { export a"$1"="active"; }
|
||||
unactive() { unset -v a"$1"; }
|
||||
activate() { export ACTIVE["$1"]=active; }
|
||||
deactivate() { unset "ACTIVE[$1]"; }
|
||||
|
||||
focus() { export f"$1"="focused"; }
|
||||
unfocus() { unset -v f"$1"; }
|
||||
focus() { export FOCUSED["$1"]=focused; }
|
||||
unfocus() { unset "FOCUSED[$1]"; }
|
||||
|
||||
workspaces() {
|
||||
for num in 1 2 3 4 5 6 7 8 9; do
|
||||
unfocus $num
|
||||
unactive $num
|
||||
unocc $num
|
||||
for s in "${spaces[@]}"; do
|
||||
unfocus "$s"
|
||||
deactivate "$s"
|
||||
unoccupy "$s"
|
||||
done
|
||||
|
||||
# TODO: avoid recomputing these each time and actually listen to the events?
|
||||
mons_json=$(hyprctl monitors -j)
|
||||
for num in $(hyprctl workspaces -j | jq -r '.[] | select(.windows > 0) | .id'); do
|
||||
occ "$num"
|
||||
occupy "$num"
|
||||
done
|
||||
|
||||
for num in $(echo "$mons_json" | jq -r '.[].activeWorkspace.id'); do
|
||||
active "$num"
|
||||
activate "$num"
|
||||
done
|
||||
|
||||
for num in $(echo "$mons_json" | jq -r '.[] | select(.focused) | .activeWorkspace.id'); do
|
||||
|
@ -38,21 +42,23 @@ workspaces() {
|
|||
# TODO: would be nice to have monitors' workspaces show up in left-to-right
|
||||
# order as laid out in physical/pixel space
|
||||
# this would make glancing at the workspace indicator more intuitive
|
||||
#
|
||||
# TODO: might be nice to exclude certain windows as counting towards "occupation" such as xwaylandvideobridge or w/e
|
||||
#
|
||||
# NOTE: maybe I can group workspaces by their monitor with some mechanism for "unassigned" workspace to show up by a "primary" monitor
|
||||
|
||||
# render eww widget
|
||||
echo "(eventbox :onscroll \"echo {} | sed -e 's/up/-1/g' -e 's/down/+1/g' | xargs hyprctl dispatch workspace\" \
|
||||
(box :class \"workspaces\" :orientation \"h\" :spacing 0 :space-evenly \"true\" \
|
||||
(button :onclick \"hyprctl dispatch workspace 1\" :onrightclick \"hyprctl dispatch workspace 1\" :class \"workspace $a1 $o1 $f1\" \"${ic[0]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 2\" :onrightclick \"hyprctl dispatch workspace 2\" :class \"workspace $a2 $o2 $f2\" \"${ic[1]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 3\" :onrightclick \"hyprctl dispatch workspace 3\" :class \"workspace $a3 $o3 $f3\" \"${ic[2]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 4\" :onrightclick \"hyprctl dispatch workspace 4\" :class \"workspace $a4 $o4 $f4\" \"${ic[3]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 5\" :onrightclick \"hyprctl dispatch workspace 5\" :class \"workspace $a5 $o5 $f5\" \"${ic[4]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 6\" :onrightclick \"hyprctl dispatch workspace 6\" :class \"workspace $a6 $o6 $f6\" \"${ic[5]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 7\" :onrightclick \"hyprctl dispatch workspace 7\" :class \"workspace $a7 $o7 $f7\" \"${ic[6]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 8\" :onrightclick \"hyprctl dispatch workspace 8\" :class \"workspace $a8 $o8 $f8\" \"${ic[7]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 9\" :onrightclick \"hyprctl dispatch workspace 9\" :class \"workspace $a9 $o9 $f9\" \"${ic[8]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 1\" :onrightclick \"hyprctl dispatch workspace 1\" :class \"workspace ${ACTIVE[1]} ${OCCUPIED[1]} ${FOCUSED[1]}\" \"${icons[0]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 2\" :onrightclick \"hyprctl dispatch workspace 2\" :class \"workspace ${ACTIVE[2]} ${OCCUPIED[2]} ${FOCUSED[2]}\" \"${icons[1]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 3\" :onrightclick \"hyprctl dispatch workspace 3\" :class \"workspace ${ACTIVE[3]} ${OCCUPIED[3]} ${FOCUSED[3]}\" \"${icons[2]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 4\" :onrightclick \"hyprctl dispatch workspace 4\" :class \"workspace ${ACTIVE[4]} ${OCCUPIED[4]} ${FOCUSED[4]}\" \"${icons[3]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 5\" :onrightclick \"hyprctl dispatch workspace 5\" :class \"workspace ${ACTIVE[5]} ${OCCUPIED[5]} ${FOCUSED[5]}\" \"${icons[4]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 6\" :onrightclick \"hyprctl dispatch workspace 6\" :class \"workspace ${ACTIVE[6]} ${OCCUPIED[6]} ${FOCUSED[6]}\" \"${icons[5]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 7\" :onrightclick \"hyprctl dispatch workspace 7\" :class \"workspace ${ACTIVE[7]} ${OCCUPIED[7]} ${FOCUSED[7]}\" \"${icons[6]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 8\" :onrightclick \"hyprctl dispatch workspace 8\" :class \"workspace ${ACTIVE[8]} ${OCCUPIED[8]} ${FOCUSED[8]}\" \"${icons[7]}\") \
|
||||
(button :onclick \"hyprctl dispatch workspace 9\" :onrightclick \"hyprctl dispatch workspace 9\" :class \"workspace ${ACTIVE[9]} ${OCCUPIED[9]} ${FOCUSED[9]}\" \"${icons[8]}\") \
|
||||
) \
|
||||
)"
|
||||
}
|
||||
|
|
2
modules/home-manager/eww/scripts/workspaces/.gitignore
vendored
Normal file
2
modules/home-manager/eww/scripts/workspaces/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
./target
|
||||
./result
|
20
modules/home-manager/eww/scripts/workspaces/default.nix
Normal file
20
modules/home-manager/eww/scripts/workspaces/default.nix
Normal file
|
@ -0,0 +1,20 @@
|
|||
{pkgs ? import <nixpkgs> {}}: let
|
||||
# lock = builtins.fromJSON (builtins.readFile ../../../../../flake.lock);
|
||||
# nixpkgsRev = lock.nodes.nixpkgs.locked.rev;
|
||||
# pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/${nixpkgsRev}.tar.gz") {};
|
||||
pname = "hyprland-workspaces-eww";
|
||||
version = "1.0.0";
|
||||
src = ./src;
|
||||
in
|
||||
pkgs.rustPlatform.buildRustPackage {
|
||||
inherit pname version src;
|
||||
cargoHash = "sha256-6Wl3cOIxlPJjzEuzNhCBZJXayL8runQfAxPruvzh2Vc=";
|
||||
# cargoHash = pkgs.lib.fakeHash;
|
||||
checkType = "release";
|
||||
postBuild = ''
|
||||
# pushd target/*/release
|
||||
# ls -la
|
||||
# ${pkgs.upx}/bin/upx --best --lzma hyprland-workspaces-eww
|
||||
# popd
|
||||
'';
|
||||
}
|
1
modules/home-manager/eww/scripts/workspaces/src/.gitignore
vendored
Normal file
1
modules/home-manager/eww/scripts/workspaces/src/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
target
|
96
modules/home-manager/eww/scripts/workspaces/src/Cargo.lock
generated
Normal file
96
modules/home-manager/eww/scripts/workspaces/src/Cargo.lock
generated
Normal file
|
@ -0,0 +1,96 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "hyprland-workspaces-eww"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
19
modules/home-manager/eww/scripts/workspaces/src/Cargo.toml
Normal file
19
modules/home-manager/eww/scripts/workspaces/src/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "hyprland-workspaces-eww"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "hyprland-workspaces-eww"
|
||||
path = "./main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0.217"
|
||||
serde_json = "1.0.137"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
186
modules/home-manager/eww/scripts/workspaces/src/main.rs
Normal file
186
modules/home-manager/eww/scripts/workspaces/src/main.rs
Normal file
|
@ -0,0 +1,186 @@
|
|||
mod workspace {
|
||||
pub struct Workspace {
|
||||
id: usize,
|
||||
icon: char,
|
||||
pub is_active: bool,
|
||||
pub is_occupied: bool,
|
||||
pub is_focused: bool,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(id: usize) -> Self {
|
||||
Self {
|
||||
id,
|
||||
icon: id.to_string().chars().next().unwrap_or('?'),
|
||||
is_active: false,
|
||||
is_occupied: false,
|
||||
is_focused: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> usize {
|
||||
return self.id;
|
||||
}
|
||||
|
||||
pub fn icon(&self) -> char {
|
||||
return self.icon;
|
||||
}
|
||||
|
||||
pub fn clear_states(&mut self) {
|
||||
self.is_active = false;
|
||||
self.is_occupied = false;
|
||||
self.is_focused = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod eww {}
|
||||
|
||||
mod hypr {
|
||||
pub mod hyprland {
|
||||
|
||||
pub mod workspace {
|
||||
pub type Id = usize;
|
||||
pub type Name = String;
|
||||
}
|
||||
|
||||
pub mod socket2 {
|
||||
use super::workspace;
|
||||
use std::{error::Error, fmt::Display, num::ParseIntError, str::FromStr};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
Workspace(workspace::Id),
|
||||
Exit(),
|
||||
WorkspaceV2(String, String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventParseError {
|
||||
UnknownEventType(String),
|
||||
MissingParameters(String),
|
||||
InvalidParameters(String, String),
|
||||
ParseIntError(ParseIntError),
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for EventParseError {
|
||||
fn from(value: ParseIntError) -> Self {
|
||||
Self::ParseIntError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for EventParseError {}
|
||||
|
||||
impl Display for EventParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EventParseError::UnknownEventType(event_type) => {
|
||||
write!(f, "unknown event type: {event_type}")
|
||||
}
|
||||
|
||||
EventParseError::MissingParameters(event_type) => {
|
||||
write!(f, "missing parameters for event type: {event_type}")
|
||||
}
|
||||
|
||||
EventParseError::ParseIntError(err) => {
|
||||
write!(f, "error parsing integer: {err}")
|
||||
}
|
||||
EventParseError::InvalidParameters(event_type, params) => {
|
||||
write!(
|
||||
f,
|
||||
"invalid parameters for event type {event_type}: {params}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Event {
|
||||
type Err = EventParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (event_type, rest): (&str, Option<&str>) = s
|
||||
.find(">>")
|
||||
.map(|n| {
|
||||
let (a, b) = s.split_at(n);
|
||||
(a, Option::Some(&b[2..]))
|
||||
})
|
||||
.unwrap_or((s, Option::None));
|
||||
match (event_type, rest) {
|
||||
("workspace", None) => {
|
||||
Err(EventParseError::MissingParameters(event_type.to_string()))
|
||||
}
|
||||
("workspace", Some(workspace)) => Ok(Event::Workspace(workspace.parse()?)),
|
||||
("workspacev2", Some(args)) => {
|
||||
let args: (String, String) = args
|
||||
.split_once(',')
|
||||
.map(|(a, b)| (a.to_string(), b.to_string()))
|
||||
.ok_or(EventParseError::InvalidParameters(
|
||||
event_type.to_string(),
|
||||
args.to_string(),
|
||||
))?;
|
||||
Ok(Event::WorkspaceV2(args.0, args.1))
|
||||
}
|
||||
("exit", _) => Ok(Event::Exit()),
|
||||
_ => Err(EventParseError::UnknownEventType(event_type.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use hypr::hyprland::socket2::Event;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
error::Error,
|
||||
io::{BufRead, BufReader},
|
||||
os::unix::net::UnixStream,
|
||||
};
|
||||
use workspace::Workspace;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut workspaces: HashMap<usize, Workspace> =
|
||||
(1..=9).map(|n| (n, Workspace::new(n))).collect();
|
||||
let path = format!(
|
||||
"{}/hypr/{}/.socket2.sock",
|
||||
env::var("XDG_RUNTIME_DIR")?,
|
||||
env::var("HYPRLAND_INSTANCE_SIGNATURE")?
|
||||
);
|
||||
|
||||
eprintln!("opening {}", path);
|
||||
let stream = UnixStream::connect(&path)?;
|
||||
let event_lines = BufReader::new(stream).lines();
|
||||
for l in event_lines.into_iter() {
|
||||
match l?.parse::<Event>() {
|
||||
Ok(e) => match e {
|
||||
Event::Workspace(i) => match workspaces.get_mut(&i) {
|
||||
Some(related_workspace) => {
|
||||
eprintln!(
|
||||
"setting workspace {} (id: {}) as active",
|
||||
related_workspace.icon(),
|
||||
related_workspace.id()
|
||||
);
|
||||
related_workspace.is_active = true
|
||||
}
|
||||
None => {
|
||||
eprintln!("event for untracked workspace {}", i);
|
||||
}
|
||||
},
|
||||
Event::Exit() => break,
|
||||
other => {
|
||||
eprintln!("unhandled event: {:?}", other);
|
||||
}
|
||||
},
|
||||
Err(e) => eprintln!("error parsing event: {}", e),
|
||||
}
|
||||
render(&workspaces)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(workspaces: &HashMap<usize, Workspace>) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
|
@ -27,7 +27,7 @@
|
|||
programs.hyprland = {
|
||||
enable = true;
|
||||
};
|
||||
environment.systemPackages = with pkgs; [hyprpaper xwaylandvideobridge socat];
|
||||
environment.systemPackages = with pkgs; [hyprpaper xwaylandvideobridge netcat-openbsd];
|
||||
|
||||
programs.hyprland = {
|
||||
package = flakeInputs.hyprland.packages.${pkgs.system}.hyprland;
|
||||
|
|
Loading…
Reference in a new issue