From 1f6e33fa75d377ad634f0068a6259be5ae869a4f Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 26 Jul 2024 11:22:22 +0200 Subject: [PATCH] Workspaces on hyprland --- src/eww.rs | 23 +++++++++++++++++ src/hyprland.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 2 ++ src/niri.rs | 25 ++---------------- src/output.rs | 10 ++++++++ 5 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 src/eww.rs create mode 100644 src/output.rs diff --git a/src/eww.rs b/src/eww.rs new file mode 100644 index 0000000..b1dd34d --- /dev/null +++ b/src/eww.rs @@ -0,0 +1,23 @@ +use eyre::{bail, Context}; + +/// Update eww bar variable +pub fn update_var(key: &str, value: &str) -> eyre::Result<()> { + println!("eww update {key}={value}"); + let output = std::process::Command::new("eww") + .arg("update") + .arg(format!("{key}={value}")) + .output() + .wrap_err("failed to execute 'eww update'")?; + + if !output.status.success() { + let stdout = std::str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8"); + let stderr = std::str::from_utf8(&output.stderr).unwrap_or("Invalid UTF-8"); + + eprintln!("'eww update' stdout: {stdout}"); + eprintln!("'eww update' stderr: {stderr}"); + + bail!("'eww update' failed. See logs."); + } + + Ok(()) +} diff --git a/src/hyprland.rs b/src/hyprland.rs index 6fc1e0a..628a816 100644 --- a/src/hyprland.rs +++ b/src/hyprland.rs @@ -1,16 +1,31 @@ -use eyre::{bail, eyre}; +use crate::{eww, output, Command}; +use eyre::{bail, eyre, Context}; +use serde::{de::DeserializeOwned, Deserialize}; +use std::str; -use crate::Command; +#[derive(Deserialize)] +struct HyprWorkspace { + name: String, + + #[serde(rename = "monitorID")] + monitor_id: u32, + // + // id, windows, monitor, hasfullscreen, lastwindow, lastwindowtitle +} pub fn handle(command: Command) -> eyre::Result<()> { match command { Command::Workspaces {} => { - std::process::Command::new("eww-workspaces") - .status() - .map_err(|e| eyre!("unga bunga: {e}"))?; + println!("{}", get_workspaces()?); } - Command::SwitchWorkspace { .. } => { - bail!("not supported on Hyprland"); + Command::SwitchWorkspace { to } => { + std::process::Command::new("hyprctl") + .args(["dispatch", "workspace"]) + .arg(format!("{to}")) + .status() + .map_err(|e| eyre!("hyprctl error: {e}"))?; + + eww::update_var("workspaces", &get_workspaces()?)?; } Command::KeyboardLayout { .. } => { bail!("not supported on Hyprland"); @@ -19,3 +34,42 @@ pub fn handle(command: Command) -> eyre::Result<()> { Ok(()) } + +fn hyprctl(args: &[&str]) -> eyre::Result { + let workspaces_output = std::process::Command::new("hyprctl") + .args(args) + .arg("-j") // JSON output + .output() + .map_err(|e| eyre!("hyprctl error: {e}"))?; + + let workspaces_stdout = str::from_utf8(&workspaces_output.stdout)?; + //let workspaces_stderr = str::from_utf8(&workspaces_output.stderr)?; + + if !workspaces_output.status.success() { + bail!("hyprctl error, non-zero exit code"); + } + + serde_json::from_str(workspaces_stdout).wrap_err("Failed to deserialize output from hyprctl") +} + +/// Get a JSON-string containing info about workspaces +fn get_workspaces() -> eyre::Result { + let active_workspace: HyprWorkspace = hyprctl(&["activeworkspace"])?; + let workspaces: Vec = hyprctl(&["workspaces"])?; + + let workspaces: output::Workspaces = workspaces + .into_iter() + .map(|workspace| { + let is_active = workspace.name == active_workspace.name; + ( + workspace.name, + output::Workspace { + monitor: workspace.monitor_id, + active: is_active, + }, + ) + }) + .collect(); + + serde_json::to_string(&workspaces).wrap_err("Failed to serialize workspaces") +} diff --git a/src/main.rs b/src/main.rs index c5df669..9b8cba9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,10 @@ use once_cell::sync::Lazy; use serde::Serialize; use std::env; +mod eww; mod hyprland; mod niri; +mod output; #[derive(Parser)] struct Opt { diff --git a/src/niri.rs b/src/niri.rs index a814673..2d3a839 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{Command, Workspace}; +use crate::{eww, Command, Workspace}; use eyre::{bail, eyre, Context}; use niri_ipc::{Socket, WorkspaceReferenceArg}; @@ -19,7 +19,7 @@ pub fn handle(command: Command) -> eyre::Result<()> { ))? .map_err(|e| eyre!("niri error: {e}"))?; - update_eww("workspaces", &get_workspaces()?)?; + eww::update_var("workspaces", &get_workspaces()?)?; } Command::KeyboardLayout { next: _ } => todo!(), } @@ -32,27 +32,6 @@ fn open_socket() -> eyre::Result { niri_ipc::Socket::connect().wrap_err("Failed to open niri socket") } -/// Update eww bar variable -fn update_eww(key: &str, value: &str) -> eyre::Result<()> { - let output = std::process::Command::new("eww") - .arg("update") - .arg(format!("{key}={value}")) - .output() - .wrap_err("failed to execute 'eww update'")?; - - if !output.status.success() { - let stdout = std::str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8"); - let stderr = std::str::from_utf8(&output.stderr).unwrap_or("Invalid UTF-8"); - - eprintln!("'eww update' stdout: {stdout}"); - eprintln!("'eww update' stderr: {stderr}"); - - bail!("'eww update' failed. See logs."); - } - - Ok(()) -} - /// Get a JSON-string containing info about workspaces fn get_workspaces() -> eyre::Result { let socket = open_socket()?; diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..0b2cfe6 --- /dev/null +++ b/src/output.rs @@ -0,0 +1,10 @@ +use serde::Serialize; +use std::collections::HashMap; + +pub type Workspaces = HashMap; + +#[derive(Clone, Debug, Serialize)] +pub struct Workspace { + pub monitor: u32, + pub active: bool, +}