142 lines
4.4 KiB
Python
Executable File
142 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""mnotifdctl — a small CLI for mnotifd's D-Bus surface, the "dunstctl" mnotifd
|
|
never had (screenrec.sh has its own note on this gap). Talks to:
|
|
|
|
org.freedesktop.Notifications (CloseNotification)
|
|
eu.abdelbaki.mnotifd.History1 (List/Get/Pop/PopLatest/Remove/Clear)
|
|
|
|
`close-all` is the one exception: it isn't a D-Bus call (mnotifd doesn't
|
|
expose window.close_all() over the bus), it's the same SIGUSR1 signal the
|
|
Super+Ctrl+C keybind already sends.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "4.0")
|
|
from gi.repository import Gio, GLib # noqa: E402
|
|
|
|
FDN_NAME = "org.freedesktop.Notifications"
|
|
FDN_PATH = "/org/freedesktop/Notifications"
|
|
FDN_IFACE = "org.freedesktop.Notifications"
|
|
HISTORY_IFACE = "eu.abdelbaki.mnotifd.History1"
|
|
|
|
|
|
def _proxy(iface: str) -> Gio.DBusProxy:
|
|
return Gio.DBusProxy.new_for_bus_sync(
|
|
Gio.BusType.SESSION, Gio.DBusProxyFlags.NONE, None,
|
|
FDN_NAME, FDN_PATH, iface, None)
|
|
|
|
|
|
def cmd_list(args: argparse.Namespace) -> int:
|
|
proxy = _proxy(HISTORY_IFACE)
|
|
(entries,) = proxy.call_sync(
|
|
"List", None, Gio.DBusCallFlags.NONE, -1, None).unpack()
|
|
if args.json:
|
|
print(json.dumps(entries, indent=2))
|
|
return 0
|
|
if not entries:
|
|
print("(history is empty)")
|
|
return 0
|
|
for e in entries:
|
|
print(f"{e['id']:>4} {(e.get('app_name') or '?'):<20} {e.get('summary', '')}")
|
|
return 0
|
|
|
|
|
|
def cmd_get(args: argparse.Namespace) -> int:
|
|
proxy = _proxy(HISTORY_IFACE)
|
|
(entry,) = proxy.call_sync(
|
|
"Get", GLib.Variant("(u)", (args.id,)), Gio.DBusCallFlags.NONE, -1, None).unpack()
|
|
print(json.dumps(entry, indent=2))
|
|
return 0
|
|
|
|
|
|
def cmd_pop(args: argparse.Namespace) -> int:
|
|
proxy = _proxy(HISTORY_IFACE)
|
|
if args.id is None:
|
|
(new_id,) = proxy.call_sync(
|
|
"PopLatest", None, Gio.DBusCallFlags.NONE, -1, None).unpack()
|
|
else:
|
|
(new_id,) = proxy.call_sync(
|
|
"Pop", GLib.Variant("(u)", (args.id,)), Gio.DBusCallFlags.NONE, -1, None).unpack()
|
|
print(new_id)
|
|
return 0
|
|
|
|
|
|
def cmd_remove(args: argparse.Namespace) -> int:
|
|
proxy = _proxy(HISTORY_IFACE)
|
|
proxy.call_sync(
|
|
"Remove", GLib.Variant("(u)", (args.id,)), Gio.DBusCallFlags.NONE, -1, None)
|
|
return 0
|
|
|
|
|
|
def cmd_clear(_args: argparse.Namespace) -> int:
|
|
proxy = _proxy(HISTORY_IFACE)
|
|
proxy.call_sync("Clear", None, Gio.DBusCallFlags.NONE, -1, None)
|
|
return 0
|
|
|
|
|
|
def cmd_close(args: argparse.Namespace) -> int:
|
|
proxy = _proxy(FDN_IFACE)
|
|
proxy.call_sync(
|
|
"CloseNotification", GLib.Variant("(u)", (args.id,)),
|
|
Gio.DBusCallFlags.NONE, -1, None)
|
|
return 0
|
|
|
|
|
|
def cmd_close_all(_args: argparse.Namespace) -> int:
|
|
# bracket trick so pkill's own argv doesn't self-match, same as the
|
|
# Super+Ctrl+C keybind in hypr/usr/binds.lua
|
|
subprocess.run(["pkill", "-USR1", "-f", "[b]eacon/main.py"])
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
p = argparse.ArgumentParser(prog="mnotifdctl", description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
sub = p.add_subparsers(dest="command", required=True)
|
|
|
|
sp = sub.add_parser("list", help="list notification history, newest first")
|
|
sp.add_argument("--json", action="store_true")
|
|
sp.set_defaults(func=cmd_list)
|
|
|
|
sp = sub.add_parser("get", help="show one history entry as JSON")
|
|
sp.add_argument("id", type=int)
|
|
sp.set_defaults(func=cmd_get)
|
|
|
|
sp = sub.add_parser(
|
|
"pop", help="re-display a history entry (most recent if id omitted)")
|
|
sp.add_argument("id", type=int, nargs="?", default=None)
|
|
sp.set_defaults(func=cmd_pop)
|
|
|
|
sp = sub.add_parser("remove", help="remove one entry from history")
|
|
sp.add_argument("id", type=int)
|
|
sp.set_defaults(func=cmd_remove)
|
|
|
|
sp = sub.add_parser("clear", help="clear all history")
|
|
sp.set_defaults(func=cmd_clear)
|
|
|
|
sp = sub.add_parser("close", help="dismiss a live notification by id")
|
|
sp.add_argument("id", type=int)
|
|
sp.set_defaults(func=cmd_close)
|
|
|
|
sp = sub.add_parser("close-all", help="dismiss every visible notification")
|
|
sp.set_defaults(func=cmd_close_all)
|
|
|
|
args = p.parse_args()
|
|
try:
|
|
return args.func(args)
|
|
except GLib.GError as e:
|
|
print(f"mnotifdctl: {e.message}", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|