diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-15 13:53:34 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-15 13:53:34 +0200 |
| commit | bcf15590f9e43f2d8978cf7cccfbdf3bb66e376f (patch) | |
| tree | bf6d77460dc7025582fc9a486d9271b176cb6e1b /cmd/notifyctl | |
| parent | 3bdd5a50c3b8fa0a885dc58a3d0ec64880f95619 (diff) | |
| download | notifyd-bcf15590f9e43f2d8978cf7cccfbdf3bb66e376f.tar.gz notifyd-bcf15590f9e43f2d8978cf7cccfbdf3bb66e376f.zip | |
feat: add the control interface and notifyctl
The private interface carries what the spec cannot: close-all, invoke action
and clear history. notifyctl reads the published files for list and history,
because the files are the interface, and uses D-Bus only for the mutations.
Diffstat (limited to 'cmd/notifyctl')
| -rw-r--r-- | cmd/notifyctl/main.go | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/cmd/notifyctl/main.go b/cmd/notifyctl/main.go new file mode 100644 index 0000000..ca1db86 --- /dev/null +++ b/cmd/notifyctl/main.go @@ -0,0 +1,127 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +package main + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + + "danix.xyz/notifyd/internal/notify" + "github.com/godbus/dbus/v5" +) + +func usage() { + fmt.Fprintf(os.Stderr, `usage: %s <verb> + list the live queue as JSON + history [n] the history ring as JSON, default 20 + close <id> dismiss one notification + close-all dismiss every notification + action <id> <key> invoke an action on a live notification + clear-history empty the history ring +`, filepath.Base(os.Args[0])) + os.Exit(2) +} + +func main() { + if len(os.Args) < 2 { + usage() + } + verb := os.Args[1] + + switch verb { + case "list": + printFile("queue.json", 0) + case "history": + limit := 20 + if len(os.Args) >= 3 { + n, err := strconv.Atoi(os.Args[2]) + if err != nil { + usage() + } + limit = n + } + printFile("history.json", limit) + case "close": + need(3) + id := parseID(os.Args[2]) + callControl("Dismiss", id) + case "close-all": + callControl("CloseAll") + case "action": + need(4) + id := parseID(os.Args[2]) + callControl("InvokeAction", id, os.Args[3]) + case "clear-history": + callControl("ClearHistory") + default: + usage() + } +} + +func need(n int) { + if len(os.Args) < n { + usage() + } +} + +func parseID(s string) uint32 { + id, err := strconv.ParseUint(s, 10, 32) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: bad id %q\n", filepath.Base(os.Args[0]), s) + os.Exit(1) + } + return uint32(id) +} + +// printFile reads a published file, because the files are the interface. +func printFile(name string, limit int) { + data, err := os.ReadFile(filepath.Join(notify.RuntimeDir(), name)) + if err != nil { + fmt.Fprintf(os.Stderr, "notifyctl: %v\n", err) + os.Exit(1) + } + if limit > 0 { + var all []notify.Popup + if err := json.Unmarshal(data, &all); err != nil { + fmt.Fprintf(os.Stderr, "notifyctl: %v\n", err) + os.Exit(1) + } + if len(all) > limit { + all = all[:limit] + } + out, _ := json.MarshalIndent(all, "", " ") + fmt.Println(string(out)) + return + } + var pretty any + json.Unmarshal(data, &pretty) + out, _ := json.MarshalIndent(pretty, "", " ") + fmt.Println(string(out)) +} + +// callControl drives the daemon over the private interface. It is the only +// thing that mutates state. +func callControl(method string, args ...any) { + conn, err := dbus.SessionBus() + if err != nil { + fmt.Fprintf(os.Stderr, "notifyctl: session bus: %v\n", err) + os.Exit(1) + } + obj := conn.Object(notify.Name, dbus.ObjectPath("/xyz/danix/Notifyd")) + if err := obj.Call("xyz.danix.Notifyd."+method, 0, args...).Err; err != nil { + fmt.Fprintf(os.Stderr, "notifyctl: %v\n", err) + os.Exit(1) + } +} |
