133 lines
3.7 KiB
Python
133 lines
3.7 KiB
Python
"""sims-cli — talk to the running sims status bar over DBus."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from dataclasses import dataclass
|
|
from typing import Callable
|
|
|
|
DEST = "org.Fabric.fabric.sims"
|
|
OBJ = "/org/Fabric/fabric"
|
|
IFACE = "org.Fabric.fabric"
|
|
|
|
|
|
def _dbus_send(*args: str) -> str:
|
|
proc = subprocess.run(
|
|
["dbus-send", "--session", "--print-reply", f"--dest={DEST}", OBJ, *args],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if proc.returncode != 0:
|
|
sys.stderr.write(proc.stderr)
|
|
sys.exit(proc.returncode)
|
|
return proc.stdout
|
|
|
|
|
|
def invoke_action(action: str) -> None:
|
|
_dbus_send(f"{IFACE}.InvokeAction", f"string:{action}", "array:string:")
|
|
|
|
|
|
_ACTION_RE = re.compile(r'dict entry\(\s*string "([^"]+)"')
|
|
|
|
|
|
def list_actions() -> list[str]:
|
|
out = _dbus_send(
|
|
"org.freedesktop.DBus.Properties.Get",
|
|
f"string:{IFACE}",
|
|
"string:Actions",
|
|
)
|
|
return _ACTION_RE.findall(out)
|
|
|
|
|
|
@dataclass
|
|
class Command:
|
|
name: str
|
|
help: str
|
|
run: Callable[[argparse.Namespace], None]
|
|
|
|
|
|
def _action(name: str) -> Callable[[argparse.Namespace], None]:
|
|
return lambda _ns: invoke_action(name)
|
|
|
|
|
|
COMMANDS: list[Command] = [
|
|
Command("finder", "open window finder", _action("open-finder")),
|
|
Command("apps", "open application launcher", _action("open-app-launcher")),
|
|
Command("power", "open power menu", _action("open-power-menu")),
|
|
Command("screenshot", "open screenshot menu", _action("open-screenshot-menu")),
|
|
Command("notmuch-refresh", "refresh unread mail count", _action("refresh-notmuch")),
|
|
]
|
|
|
|
|
|
def _cmd_screenrec(ns: argparse.Namespace) -> None:
|
|
mapping = {
|
|
"menu": "open-screenrec-menu",
|
|
"start-monitor": "screenrec-start-monitor",
|
|
"start-region": "screenrec-start-region",
|
|
"stop": "screenrec-stop",
|
|
}
|
|
invoke_action(mapping[ns.screenrec_cmd])
|
|
|
|
|
|
def _cmd_list(ns: argparse.Namespace) -> None:
|
|
actions = list_actions()
|
|
if ns.json:
|
|
json.dump(actions, sys.stdout, indent=2)
|
|
sys.stdout.write("\n")
|
|
else:
|
|
for a in actions:
|
|
print(a)
|
|
|
|
|
|
def _cmd_invoke(ns: argparse.Namespace) -> None:
|
|
invoke_action(ns.action)
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(
|
|
prog="sims-cli",
|
|
description="Talk to the running sims status bar over DBus.",
|
|
)
|
|
sub = parser.add_subparsers(dest="cmd", required=True, metavar="COMMAND")
|
|
|
|
for cmd in COMMANDS:
|
|
p = sub.add_parser(cmd.name, help=cmd.help)
|
|
p.set_defaults(func=cmd.run)
|
|
|
|
rec = sub.add_parser("screenrec", help="screen recording controls")
|
|
rec_sub = rec.add_subparsers(dest="screenrec_cmd", required=True, metavar="ACTION")
|
|
for sub_name, sub_help in [
|
|
("menu", "open screenrec menu (auto-detects state)"),
|
|
("start-monitor", "start recording the focused monitor"),
|
|
("start-region", "start recording a slurp-selected region"),
|
|
("stop", "stop active recording"),
|
|
]:
|
|
rec_sub.add_parser(sub_name, help=sub_help)
|
|
rec.set_defaults(func=_cmd_screenrec)
|
|
|
|
lst = sub.add_parser("list", help="list registered actions")
|
|
lst.add_argument("--json", action="store_true", help="emit JSON array")
|
|
lst.set_defaults(func=_cmd_list)
|
|
|
|
inv = sub.add_parser(
|
|
"invoke",
|
|
help="invoke a raw action by name (escape hatch for actions without a dedicated subcommand)",
|
|
)
|
|
inv.add_argument("action", help="dbus action name, e.g. open-finder")
|
|
inv.set_defaults(func=_cmd_invoke)
|
|
|
|
return parser
|
|
|
|
|
|
def main() -> None:
|
|
parser = build_parser()
|
|
ns = parser.parse_args()
|
|
ns.func(ns)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|