get app icons for each workspace

This commit is contained in:
samantha42
2026-04-23 05:43:05 +02:00
parent 52fa42698f
commit f7d6f07e3a
9 changed files with 334 additions and 106 deletions

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env bash
OUTPUT="${1:-DP-2}"
get_socket() {
local runtime="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
local sig
sig=$(ls "$runtime/hypr/" 2>/dev/null | head -n1)
echo "$runtime/hypr/$sig/.socket2.sock"
}
emit() {
local monitor_ws title
monitor_ws=$(hyprctl monitors -j 2>/dev/null \
| jq -r ".[] | select(.name==\"$OUTPUT\") | .activeWorkspace.id")
title=$(hyprctl clients -j 2>/dev/null \
| jq -r ".[] | select(.workspace.id==$monitor_ws and .focusHistoryID==0) | .title" \
| head -n1)
printf '%s\n' "${title:-}"
}
emit
SOCKET=$(get_socket)
socat -u "UNIX-CONNECT:$SOCKET" - \
| stdbuf -oL grep -E "^(activewindow|focusedmon|workspace|closewindow)>" \
| while IFS= read -r _; do
sleep 0.05
emit
done

View File

@@ -1,24 +0,0 @@
#!/usr/bin/env bash
OUTPUT="${1:-DP-2}"
emit() {
local monitor title
monitor=$(hyprctl monitors -j 2>/dev/null \
| jq -r ".[] | select(.name==\"$OUTPUT\") | .activeWorkspace.id")
title=$(hyprctl clients -j 2>/dev/null \
| jq -r ".[] | select(.workspace.id==$monitor and .focusHistoryID==0) | .title" \
| head -n1)
printf '%s\n' "${title:-}"
}
emit
socat -u "UNIX-CONNECT:/tmp/hypr/${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock" - \
| stdbuf -oL grep -E "^(activewindow|focusedmon|workspace|closewindow)>" \
| while IFS= read -r _; do
sleep 0.05
emit
done

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# get-workspace-icons.sh <workspace_id>
WORKSPACE=${1:-1}
# Get app classes on the workspace
CLASSES=$(hyprctl clients -j | jq -r \
"[.[] | select(.workspace.id == $WORKSPACE) | .class] | unique[]")
for class in $CLASSES; do
echo "=== $class ==="
# Find matching .desktop file (case-insensitive)
DESKTOP=$(find /usr/share/applications ~/.local/share/applications \
-name "*.desktop" 2>/dev/null | \
xargs grep -li "^Name.*=$class\|^Exec.*$class\|^\[Desktop Entry\]" 2>/dev/null | \
head -1)
if [[ -n "$DESKTOP" ]]; then
ICON_NAME=$(grep "^Icon=" "$DESKTOP" | cut -d= -f2)
echo " Icon name: $ICON_NAME"
# Find actual icon file
ICON_FILE=$(find /usr/share/icons ~/.local/share/icons /usr/share/pixmaps \
-name "${ICON_NAME}.*" \( -name "*.png" -o -name "*.svg" \) \
2>/dev/null | sort | tail -1)
echo " Icon file: ${ICON_FILE:-not found}"
else
echo " .desktop file not found"
fi
done

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env python3
import subprocess, json, os, socket
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
OUTPUT = None # set via argv, e.g. "DP-2"
def get_icon(cls: str) -> str | None:
theme = Gtk.IconTheme.get_default()
for name in [cls, cls.lower(), cls.lower().rstrip("0123456789")]:
info = theme.lookup_icon(name, 16, 0)
if info:
return info.get_filename()
return None
# Define which workspace IDs belong to each monitor
MONITOR_WORKSPACES = {
"DP-2": [1, 2, 3, 4, 5],
"HDMI-A-1": [6, 7, 8, 9, 10],
}
def build():
clients = json.loads(subprocess.check_output(["hyprctl", "clients", "-j"]))
workspaces = json.loads(subprocess.check_output(["hyprctl", "workspaces", "-j"]))
active = json.loads(subprocess.check_output(["hyprctl", "activeworkspace", "-j"]))
# existing workspace IDs on this monitor (Hyprland only lists non-empty ones)
existing = {w["id"] for w in workspaces if w["monitor"] == OUTPUT}
# all IDs we want to show, including empty ones
all_ids = sorted(set(MONITOR_WORKSPACES.get(OUTPUT, [])) | existing)
# build icon map
amount = {}
icons: dict[int, list[str]] = {}
for c in clients:
wid = c["workspace"]["id"]
cls = c.get("class") or c.get("initialClass", "")
path = get_icon(cls)
if path and path not in icons.get(wid, []):
icons.setdefault(wid, []).append(path)
amount[c["workspace"]["id"]] =+ 1
result = []
for wid in all_ids:
result.append({
"id": wid,
"label": str(wid),
"active": wid == active["id"],
"urgent": any(c.get("urgent") and c["workspace"]["id"] == wid for c in clients),
"output": OUTPUT,
"icons": icons.get(wid, []),
"pxwidth": 32 + len(icons.get(wid, [])) * 20,
})
print(json.dumps(result), flush=True)
def watch():
xdg = os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
sig = os.environ.get("HYPRLAND_INSTANCE_SIGNATURE", "")
sock = f"{xdg}/hypr/{sig}/.socket2.sock"
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
s.connect(sock)
buf = ""
while True:
buf += s.recv(4096).decode()
while "\n" in buf:
line, buf = buf.split("\n", 1)
ev = line.split(">>")[0]
if ev in {"workspace", "openwindow", "closewindow",
"movewindow", "urgent", "focusedmon", "activelayout"}:
build()
if __name__ == "__main__":
import sys
OUTPUT = sys.argv[1] if len(sys.argv) > 1 else None
build()
watch()

View File

@@ -7,30 +7,39 @@ else
IDS=(6 7 8 9 10)
fi
emit() {
local active urgent result
active=$(hyprctl activeworkspace -j 2>/dev/null | jq -r '.id')
urgent=$(hyprctl clients -j 2>/dev/null \
| jq -r '[.[] | select(.urgent==true) | .workspace.id] | unique | .[]')
result="["
for id in "${IDS[@]}"; do
local is_active="false"
local is_urgent="false"
[[ "$id" == "$active" ]] && is_active="true"
grep -qx "$id" <<< "$urgent" && is_urgent="true"
result+="{\"id\":$id,\"label\":\"$id\",\"active\":$is_active,\"urgent\":$is_urgent,\"output\":\"$OUTPUT\"},"
done
printf '%s\n' "${result%,}]"
# find the socket dynamically instead of relying on env var
get_socket() {
local runtime="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
local sig
sig=$(ls "$runtime/hypr/" 2>/dev/null | head -n1)
echo "$runtime/hypr/$sig/.socket2.sock"
}
emit() {
local active urgent_ids json
active=$(hyprctl activeworkspace -j 2>/dev/null | jq -r '.id')
urgent_ids=$(hyprctl clients -j 2>/dev/null \
| jq -r '[.[] | select(.urgent==true) | .workspace.id] | unique | .[]')
json=$(for id in "${IDS[@]}"; do
is_active=false
is_urgent=false
[[ "$id" == "$active" ]] && is_active=true
grep -qx "$id" <<< "$urgent_ids" && is_urgent=true
printf '{"id":%s,"label":"%s","active":%s,"urgent":%s,"output":"%s"}\n' \
"$id" "$id" "$is_active" "$is_urgent" "$OUTPUT"
done | jq -sc '.')
printf '%s\n' "$json"
}
# emit once on start
emit
# listen for events and re-emit
socat -u "UNIX-CONNECT:/tmp/hypr/${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock" - \
SOCKET=$(get_socket)
echo "Using socket: $SOCKET" >&2
socat -u "UNIX-CONNECT:$SOCKET" - \
| stdbuf -oL grep -E "^(workspace|focusedmon|activewindow|urgent|createworkspace|destroyworkspace)>" \
| while IFS= read -r _; do
sleep 0.05