93 lines
3.3 KiB
Python
93 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""beacon — the Cosmonaut Shell notification daemon for hyprdrive.
|
|
|
|
Replaces dunst: owns org.freedesktop.Notifications and renders each notification
|
|
as a holographic card (scanline/sweep/noise overlay, emitted-magenta text, violet
|
|
holo-glass, radio-squiggle divider) in a top-centre layer-shell stack, so
|
|
notifications read as one more orbit of the astro-menu / orbit-menu / station-bar
|
|
look instead of a plain popup.
|
|
|
|
main.py run the resident daemon (owns the notification bus name, stays hidden
|
|
until a notification arrives)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import signal
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# beacon-start.sh LD_PRELOADs libgtk4-layer-shell (load-ordering requirement ahead
|
|
# of libwayland-client). Drop it once resident so it isn't inherited by anything
|
|
# this process launches (same rationale as the rest of the suite's main.py).
|
|
os.environ.pop("LD_PRELOAD", None)
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
|
|
import gi # noqa: E402
|
|
|
|
gi.require_version("Gtk", "4.0")
|
|
from gi.repository import Gio, GLib, Gtk # noqa: E402
|
|
|
|
import theme # noqa: E402
|
|
from paths import APP_ID, FDN_NAME # noqa: E402
|
|
from server import NotificationServer # noqa: E402
|
|
from window import BeaconWindow # noqa: E402
|
|
|
|
|
|
class BeaconApp(Gtk.Application):
|
|
def __init__(self) -> None:
|
|
super().__init__(application_id=APP_ID,
|
|
flags=Gio.ApplicationFlags.DEFAULT_FLAGS)
|
|
self.window: BeaconWindow | None = None
|
|
self._server: NotificationServer | None = None
|
|
self._name_id = 0
|
|
|
|
def do_startup(self) -> None:
|
|
Gtk.Application.do_startup(self)
|
|
theme.load_css()
|
|
self.window = BeaconWindow(self, on_closed=self._on_closed)
|
|
# Own the freedesktop notification name; the server is created once the bus
|
|
# connection is in hand.
|
|
self._name_id = Gio.bus_own_name(
|
|
Gio.BusType.SESSION, FDN_NAME, Gio.BusNameOwnerFlags.NONE,
|
|
self._on_bus_acquired, None, self._on_name_lost)
|
|
# SIGUSR1 = close every visible card (the `Super+Ctrl+C` keybind, which
|
|
# used to run `dunstctl close-all`).
|
|
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGUSR1,
|
|
self._on_sigusr1)
|
|
self.hold() # stay alive with no visible window
|
|
|
|
def _on_sigusr1(self) -> bool:
|
|
if self.window is not None:
|
|
self.window.close_all()
|
|
return GLib.SOURCE_CONTINUE
|
|
|
|
def _on_bus_acquired(self, connection: Gio.DBusConnection, _name: str) -> None:
|
|
assert self.window is not None
|
|
self._server = NotificationServer(connection, self.window)
|
|
|
|
def _on_closed(self, nid: int, reason: int) -> None:
|
|
if self._server is not None:
|
|
self._server.emit_closed(nid, reason)
|
|
|
|
def _on_name_lost(self, _connection, _name: str) -> None:
|
|
# Another notification daemon (e.g. a still-running dunst) already owns it.
|
|
sys.stderr.write(
|
|
"beacon: could not acquire org.freedesktop.Notifications "
|
|
"(another notification daemon is running); exiting.\n")
|
|
self.quit()
|
|
|
|
def do_activate(self) -> None:
|
|
pass # resident daemon: nothing to do on activate
|
|
|
|
|
|
def main() -> int:
|
|
GLib.set_prgname("beacon")
|
|
return BeaconApp().run(sys.argv)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|