aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/desktop/eww/eww.mustache.scss4
-rw-r--r--modules/desktop/eww/system-stats.py91
-rw-r--r--modules/desktop/eww/yuck.nix155
3 files changed, 189 insertions, 61 deletions
diff --git a/modules/desktop/eww/eww.mustache.scss b/modules/desktop/eww/eww.mustache.scss
index 52049af..5004e65 100644
--- a/modules/desktop/eww/eww.mustache.scss
+++ b/modules/desktop/eww/eww.mustache.scss
@@ -45,6 +45,10 @@ window#eww-bar {
border: none;
}
+.system-item .system-label.small {
+ font-size: 12px;
+}
+
.workspace-button.active, .window-button.focused {
border-color: #{{base07}};
}
diff --git a/modules/desktop/eww/system-stats.py b/modules/desktop/eww/system-stats.py
new file mode 100644
index 0000000..380dc25
--- /dev/null
+++ b/modules/desktop/eww/system-stats.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+"""system-stats: emit {cpu, ram, net} as JSON. CPU/RAM sample on an interval
+inside one long-lived process (no per-tick spawn)"""
+import json
+import subprocess
+import threading
+import time
+
+STATE_LOCK = threading.Lock()
+STATE = {"cpu": 0, "ram": 0, "net": "down"}
+
+
+def read_cpu_times():
+ with open("/proc/stat") as f:
+ parts = f.readline().split()[1:]
+ vals = list(map(int, parts))
+ idle = vals[3] + vals[4] # idle + iowait
+ total = sum(vals)
+ return idle, total
+
+
+def cpu_ram_loop(interval=2.0):
+ prev_idle, prev_total = read_cpu_times()
+ while True:
+ time.sleep(interval)
+ idle, total = read_cpu_times()
+ d_idle = idle - prev_idle
+ d_total = total - prev_total
+ prev_idle, prev_total = idle, total
+ cpu_pct = round((1 - d_idle / d_total) * 100) if d_total else 0
+
+ with open("/proc/meminfo") as f:
+ meminfo = {}
+ for line in f:
+ k, v, *_ = line.split()
+ meminfo[k.rstrip(":")] = int(v)
+ total_kb = meminfo["MemTotal"]
+ avail_kb = meminfo["MemAvailable"]
+ ram_pct = round((1 - avail_kb / total_kb) * 100) if total_kb else 0
+
+ with STATE_LOCK:
+ STATE["cpu"] = cpu_pct
+ STATE["ram"] = ram_pct
+ emit()
+
+
+def check_net_status() -> str:
+ try:
+ with open("/run/systemd/netif/state") as f:
+ state = dict(
+ line.strip().split("=", 1) for line in f if "=" in line
+ )
+ except FileNotFoundError:
+ return "down"
+
+ oper = state.get("OPER_STATE", "off")
+ if oper == "routable":
+ return "up"
+ if oper in ("degraded", "carrier", "degraded-carrier"):
+ return "limited"
+ return "down"
+
+
+def net_loop():
+ with STATE_LOCK:
+ STATE["net"] = check_net_status()
+ emit()
+
+ proc = subprocess.Popen(
+ ["inotifywait", "-m", "-e", "modify", "/run/systemd/netif/state"],
+ stdout=subprocess.PIPE, text=True, bufsize=1,
+ )
+ for _ in proc.stdout:
+ with STATE_LOCK:
+ STATE["net"] = check_net_status()
+ emit()
+
+
+def emit():
+ with STATE_LOCK:
+ print(json.dumps(STATE), flush=True)
+
+
+def main():
+ threading.Thread(target=cpu_ram_loop, daemon=True).start()
+ threading.Thread(target=net_loop, daemon=True).start()
+ threading.Event().wait() # keep main thread alive
+
+
+if __name__ == "__main__":
+ main()
diff --git a/modules/desktop/eww/yuck.nix b/modules/desktop/eww/yuck.nix
index d522d9b..03de4bf 100644
--- a/modules/desktop/eww/yuck.nix
+++ b/modules/desktop/eww/yuck.nix
@@ -8,71 +8,91 @@
config = lib.mkIf config.desktop.enable {
programs.eww.yuckConfig =
let
- workspaces =
- let
- pythonEnv = pkgs.python3.withPackages (ps: [ ps.pygobject3 ]);
- in
- pkgs.stdenv.mkDerivation {
- pname = "eww-workspaces";
- version = "0.1.0";
- src = ./workspaces.py;
- dontUnpack = true;
- meta.mainProgram = "eww-workspaces";
-
- nativeBuildInputs = [
- pkgs.wrapGAppsHook3
- pkgs.gobject-introspection
- ];
- buildInputs = [ pkgs.gtk3 ];
-
- installPhase = ''
- mkdir -p $out/bin
- install -m755 $src $out/bin/eww-workspaces
- patchShebangs $out/bin/eww-workspaces
- '';
-
- # wrapGAppsHook3 wraps every executable in $out/bin automatically,
- # setting GI_TYPELIB_PATH etc. from buildInputs' closure.
- preFixup = ''
- gappsWrapperArgs+=(--prefix PATH : "${pythonEnv}/bin")
- '';
- };
-
- activate-windows =
- let
- pythonEnv = pkgs.python3.withPackages (ps: [ ps.pygobject3 ]);
- in
- pkgs.stdenv.mkDerivation {
- pname = "eww-activate-windows";
- version = "0.1.0";
- src = ./activate-windows.py;
- dontUnpack = true;
- meta.mainProgram = "eww-activate-windows";
-
- nativeBuildInputs = [
- pkgs.wrapGAppsHook3
- pkgs.gobject-introspection
- ];
- buildInputs = [ pkgs.gtk3 ];
-
- installPhase = ''
- mkdir -p $out/bin
- install -m755 $src $out/bin/eww-activate-windows
- patchShebangs $out/bin/eww-activate-windows
- '';
-
- # wrapGAppsHook3 wraps every executable in $out/bin automatically,
- # setting GI_TYPELIB_PATH etc. from buildInputs' closure.
- preFixup = ''
- gappsWrapperArgs+=(--prefix PATH : "${pythonEnv}/bin")
- '';
- };
+ pythonEnv = pkgs.python3.withPackages (ps: [ ps.pygobject3 ]);
+ system-stats = pkgs.stdenv.mkDerivation {
+ pname = "eww-system-stats";
+ version = "0.1.0";
+ src = ./system-stats.py;
+ dontUnpack = true;
+ meta.mainProgram = "eww-system-stats";
+
+ nativeBuildInputs = [
+ pkgs.wrapGAppsHook3
+ pkgs.gobject-introspection
+ ];
+ buildInputs = [ pkgs.gtk3 ];
+
+ installPhase = ''
+ mkdir -p $out/bin
+ install -m755 $src $out/bin/eww-system-stats
+ patchShebangs $out/bin/eww-system-stats
+ '';
+
+ # wrapGAppsHook3 wraps every executable in $out/bin automatically,
+ # setting GI_TYPELIB_PATH etc. from buildInputs' closure.
+ preFixup = ''
+ gappsWrapperArgs+=(--prefix PATH : "${pythonEnv}/bin")
+ '';
+ };
+
+ workspaces = pkgs.stdenv.mkDerivation {
+ pname = "eww-workspaces";
+ version = "0.1.0";
+ src = ./workspaces.py;
+ dontUnpack = true;
+ meta.mainProgram = "eww-workspaces";
+
+ nativeBuildInputs = [
+ pkgs.wrapGAppsHook3
+ pkgs.gobject-introspection
+ ];
+ buildInputs = [ pkgs.gtk3 ];
+
+ installPhase = ''
+ mkdir -p $out/bin
+ install -m755 $src $out/bin/eww-workspaces
+ patchShebangs $out/bin/eww-workspaces
+ '';
+
+ # wrapGAppsHook3 wraps every executable in $out/bin automatically,
+ # setting GI_TYPELIB_PATH etc. from buildInputs' closure.
+ preFixup = ''
+ gappsWrapperArgs+=(--prefix PATH : "${pythonEnv}/bin")
+ '';
+ };
+
+ activate-windows = pkgs.stdenv.mkDerivation {
+ pname = "eww-activate-windows";
+ version = "0.1.0";
+ src = ./activate-windows.py;
+ dontUnpack = true;
+ meta.mainProgram = "eww-activate-windows";
+
+ nativeBuildInputs = [
+ pkgs.wrapGAppsHook3
+ pkgs.gobject-introspection
+ ];
+ buildInputs = [ pkgs.gtk3 ];
+
+ installPhase = ''
+ mkdir -p $out/bin
+ install -m755 $src $out/bin/eww-activate-windows
+ patchShebangs $out/bin/eww-activate-windows
+ '';
+
+ # wrapGAppsHook3 wraps every executable in $out/bin automatically,
+ # setting GI_TYPELIB_PATH etc. from buildInputs' closure.
+ preFixup = ''
+ gappsWrapperArgs+=(--prefix PATH : "${pythonEnv}/bin")
+ '';
+ };
in
''
(defpoll time :interval "10s" "date '+%H:%M'")
(defpoll date :interval "60s" "date '+%d/%m'")
(deflisten workspaces :initial "[]" "${lib.getExe workspaces}")
(deflisten active-windows :initial "[]" "${lib.getExe activate-windows}")
+ (deflisten system_stats :initial "{\"cpu\":0,\"ram\":0,\"net\":\"down\"}" "${lib.getExe system-stats}")
(defwindow bar
:monitor 0
@@ -123,8 +143,21 @@
(defwidget system-widget []
(box :orientation "v" :space-evenly false :spacing 6 :halign "fill" :class "system"
- (label :class "system-item" :text time)
- (label :class "system-item" :text date)))
+ (box :orientation "v" :class "system-item" :space-evenly false :spacing 4
+ (label :class "system-icon" :text "''${system_stats.net == "up" ? "󰤨 " : "󰤭 "}")
+ (label :class "system-label" :text ""))
+ (box :orientation "v" :class "system-item" :space-evenly false :spacing 4
+ (label :class "system-icon" :text " ")
+ (label :class "system-label" :text "''${system_stats.cpu}%"))
+ (box :orientation "v" :class "system-item" :space-evenly false :spacing 4
+ (label :class "system-icon" :text " ")
+ (label :class "system-label" :text "''${system_stats.ram}%"))
+ (box :orientation "v" :class "system-item" :space-evenly false :spacing 4
+ (label :class "system-icon" :text "󰥔 ")
+ (label :class "system-label small" :text time))
+ (box :orientation "v" :class "system-item" :space-evenly false :spacing 4
+ (label :class "system-icon" :text "")
+ (label :class "system-label small" :text date))))
'';
};
}