#!/usr/bin/env python3
import os
import socket
import sys
import json
import subprocess
import html
import re
# Configuration
MAX_TITLE_LEN = 80
def get_hyprland_data(command):
try:
output = subprocess.check_output(["hyprctl", command, "-j"], text=True)
return json.loads(output)
except Exception:
return {}
def print_status():
# 1. Get Active Window
window = get_hyprland_data("activewindow")
# Defaults for "Desktop" state
top_line = "Desktop"
bottom_line = ""
# 2. Determine State
if window and window.get("address"):
# CASE A: Window is active
top_line = window.get("class", "Unknown") or "Unknown"
title = window.get("title", "") or ""
# Safely get class (Hyprland may return null)
app_class = (window.get("class") or "").lower()
# Remove Discord/Vesktop unread counter like "(209) "
if "discord" in app_class or "vesktop" in app_class:
title = re.sub(r"^\(\d+\)\s*", "", title)
title = re.sub(r"^Discord\s*\|\s*", "", title)
if len(title) > MAX_TITLE_LEN:
title = title[:MAX_TITLE_LEN - 3]
bottom_line = title
else:
# CASE B: Desktop / Empty Workspace
workspace = get_hyprland_data("activeworkspace")
ws_id = workspace.get("id", "1")
top_line = f"Workspace {ws_id}"
bottom_line = "\u00A0"
# 3. Sanitize for Pango
top_line = html.escape(top_line)
bottom_line = html.escape(bottom_line)
# 4. Format with Pango Markup
text = (
f"{top_line}\n "
f"{bottom_line}"
)
# 5. Output JSON
print(json.dumps({
"text": text,
"class": "custom-window",
"tooltip": f"{top_line}: {bottom_line}"
}), flush=True)
# Initial Run
print_status()
# Listen to Hyprland Socket for instant updates
hypr_sig = os.getenv("HYPRLAND_INSTANCE_SIGNATURE")
if not hypr_sig:
sys.exit(1)
socket_path = f"/tmp/hypr/{hypr_sig}/.socket2.sock"
if os.path.exists(socket_path):
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
client.connect(socket_path)
while True:
data = client.recv(1024).decode("utf-8", errors="ignore")
if not data:
break
if "activewindow>>" in data or "workspace>>" in data:
print_status()