// Copyright (C) 2026 Danilo M. // // 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 notify import ( "encoding/json" "os" "path/filepath" ) // RuntimeDir is where the daemon publishes. It is tmpfs, so a reboot clears // every file and there is no cleanup code. func RuntimeDir() string { if d := os.Getenv("XDG_RUNTIME_DIR"); d != "" { return filepath.Join(d, "notifyd") } return filepath.Join(os.TempDir(), "notifyd") } // Publish writes the live queue and the history ring, each whole, through a // temporary file and a rename. A reader never sees a half-written value, the // same atomic write the status registry relies on. func Publish(dir string, live, history []Popup) error { if err := os.MkdirAll(dir, 0o700); err != nil { return err } if live == nil { live = []Popup{} } if history == nil { history = []Popup{} } if err := writeJSON(filepath.Join(dir, "queue.json"), live); err != nil { return err } return writeJSON(filepath.Join(dir, "history.json"), history) } func writeJSON(path string, v any) error { data, err := json.Marshal(v) if err != nil { return err } tmp, err := os.CreateTemp(filepath.Dir(path), ".tmp-*") if err != nil { return err } if _, err := tmp.Write(data); err != nil { tmp.Close() os.Remove(tmp.Name()) return err } if err := tmp.Close(); err != nil { os.Remove(tmp.Name()) return err } return os.Rename(tmp.Name(), path) }