aboutsummaryrefslogtreecommitdiffstats
path: root/deckit/modules/windows.py
diff options
context:
space:
mode:
Diffstat (limited to 'deckit/modules/windows.py')
-rw-r--r--deckit/modules/windows.py80
1 files changed, 41 insertions, 39 deletions
diff --git a/deckit/modules/windows.py b/deckit/modules/windows.py
index 5457b16..1907b59 100644
--- a/deckit/modules/windows.py
+++ b/deckit/modules/windows.py
@@ -14,7 +14,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-"""Hyprland windows, workspaces, and launching config-declared commands."""
+"""Hyprland windows, workspaces, and launching config-listed apps."""
import json
import re
@@ -24,7 +24,8 @@ from pathlib import Path
from ..module import Module, ModuleError
ADDR = re.compile(r"0x[0-9a-f]{1,16}")
-DESKTOP_BASES = (Path("/usr/share/applications"), Path.home() / ".local/share/applications")
+# The user's own entries first, so they win over the system's, as in a launcher.
+DESKTOP_BASES = (Path.home() / ".local/share/applications", Path("/usr/share/applications"))
def _run(cmd, timeout=2):
@@ -46,40 +47,47 @@ def _dispatch(cmd):
pass
-def _desktop_icons(bases=DESKTOP_BASES) -> dict:
- """Window class -> candidate icon names, from the installed .desktop files."""
- mapping = {}
+def _desktop_entries(bases=DESKTOP_BASES) -> dict:
+ """Desktop id -> {name, icon, wmclass}, from each [Desktop Entry] section."""
+ entries = {}
for base in bases:
if not base.is_dir():
continue
for entry in base.glob("*.desktop"):
+ if entry.stem in entries:
+ continue
try:
text = entry.read_text(errors="replace")
except OSError:
continue
- section = None
- icon = wmclass = None
+ found, inside = {}, False
for line in text.splitlines():
line = line.strip()
- if line.startswith("[") and line.endswith("]"):
- section = line
- if section != "[Desktop Entry]":
+ if line.startswith("["):
+ if inside:
break
- continue
- if section != "[Desktop Entry]":
- continue
- if line.startswith("Icon="):
- icon = line.split("=", 1)[1].strip()
- elif line.startswith("StartupWMClass="):
- wmclass = line.split("=", 1)[1].strip()
- if not icon:
- continue
- candidates = [icon]
- if wmclass and wmclass != icon:
- candidates.append(wmclass)
- for key in (entry.stem, wmclass):
- if key:
- mapping.setdefault(key.lower(), candidates)
+ inside = line == "[Desktop Entry]"
+ elif inside and "=" in line:
+ key, value = line.split("=", 1)
+ if key in ("Name", "Icon", "StartupWMClass"):
+ found.setdefault(key, value.strip())
+ entries[entry.stem] = {"name": found.get("Name"), "icon": found.get("Icon"),
+ "wmclass": found.get("StartupWMClass")}
+ return entries
+
+
+def _desktop_icons(entries: dict) -> dict:
+ """Window class -> candidate icon names, by desktop id and StartupWMClass."""
+ mapping = {}
+ for stem, e in entries.items():
+ if not e["icon"]:
+ continue
+ candidates = [e["icon"]]
+ if e["wmclass"] and e["wmclass"] != e["icon"]:
+ candidates.append(e["wmclass"])
+ for key in (stem, e["wmclass"]):
+ if key:
+ mapping.setdefault(key.lower(), candidates)
return mapping
@@ -124,14 +132,10 @@ def _state(run):
def make(cfg: dict) -> Module:
run = cfg.get("run", _run)
lua = hypr_lua(cfg)
- launch = cfg.get("launch", {})
+ apps = [str(a) for a in cfg.get("launch", {}).get("apps", [])]
workspaces = cfg.get("windows", {}).get("workspaces", list(range(1, 11)))
- icons = _desktop_icons()
-
- def launch_spec(spec):
- if isinstance(spec, dict):
- return spec.get("command"), spec.get("icon"), spec.get("label")
- return spec, None, None
+ entries = _desktop_entries()
+ icons = _desktop_icons(entries)
def workspace(body):
n = body.get("n")
@@ -164,21 +168,19 @@ def make(cfg: dict) -> Module:
_dispatch(["hyprctl", "dispatch", "movetoworkspacesilent", f"{n},address:{addr}"])
def launch_key(body):
+ # Only a configured, installed desktop id; gtk-launch runs its Exec.
key = str(body.get("key", ""))
- command, _, _ = launch_spec(launch.get(key))
- if command is None:
+ if key not in apps or key not in entries:
raise ModuleError("unknown launch key")
- _popen(["sh", "-c", command] if isinstance(command, str) else command)
+ _popen(["gtk-launch", key])
def state():
st = _state(run)
for win in st["windows"]:
win["icons"] = icons.get(win["class"].lower(), [win["class"].lower()])
known = sorted(set(int(n) for n in workspaces) | set(st["workspaces"]))
- launches = []
- for key, spec in launch.items():
- _, icon, label = launch_spec(spec)
- launches.append({"key": key, "icon": icon, "label": label or key})
+ launches = [{"key": a, "icon": entries[a]["icon"], "label": entries[a]["name"] or a}
+ for a in apps if a in entries]
return {**st, "workspaces": known, "launches": launches}
return Module(name="windows", title="Windows", state=state,