diff --git a/.config/sway/config b/.config/sway/config index f431603..8d8adba 100644 --- a/.config/sway/config +++ b/.config/sway/config @@ -188,6 +188,9 @@ input type:touchpad { # Move focus to the parent container bindsym $wm_mod+bracketleft focus parent bindsym $wm_mod+bracketright focus child + + # Select urgent window to move focus to + bindsym $wm_mod+backslash exec select-urgent # # Scratchpad: # diff --git a/.local/bin/select-urgent b/.local/bin/select-urgent new file mode 100755 index 0000000..69c44c0 --- /dev/null +++ b/.local/bin/select-urgent @@ -0,0 +1,61 @@ +#! /usr/bin/python3 + +import json +import re +import subprocess +from collections.abc import Iterable, Iterator +from typing import Any + + +def urgent_descendents(node: dict[str, Any]) -> Iterator[dict[str, Any]]: + for subnode in node["nodes"]: + yield from find_urgent(subnode) + + +def find_urgent(node: dict[str, Any]) -> Iterator[dict[str, Any]]: + if node["type"] in ("root", "output"): + yield from urgent_descendents(node) + elif node["type"] == "workspace": + for unode in urgent_descendents(node): + yield unode | {"workspace": node["name"]} + elif node["type"] == "con": + if node["focus"]: + yield from urgent_descendents(node) + elif node["urgent"]: + yield {"id": node["id"], "name": node["name"]} + + +def display_option(unode: dict[str, Any]) -> str: + return f"[#{unode['id']} @ WS {unode['workspace']}] {unode['name']}" + + +def select_option(unodes: Iterable[dict[str, Any]]) -> str: + joined = "".join(display_option(unode) + "\n" for unode in unodes) + proc = subprocess.run( + ["fuzzel", "--dmenu"], + input=joined, + capture_output=True, + text=True, + ) + + return proc.stdout + + +def parse_id_from_option(opt: str) -> int: + m = re.match("^\\[#(?P[0-9]+) ", opt) + if m is None: + raise ValueError() + + return int(m["id"]) + + +def main() -> None: + proc = subprocess.run(["swaymsg", "-t", "get_tree"], capture_output=True, text=True) + sway_tree = json.loads(proc.stdout) + opt = select_option(find_urgent(sway_tree)) + con_id = parse_id_from_option(opt) + subprocess.run(["swaymsg", "--raw", f"[con_id={con_id}]", "focus"]) + + +if __name__ == "__main__": + main()