moonset/journal.md
nevaforget e66ef76b4d feat: rewrite moonset in Rust (gtk4-rs + gtk4-layer-shell)
Feature-parity with Python v0.2.0. Same CSS, same UI, same actions.
Single 3.1 MB binary with embedded resources (CSS, wallpaper, avatar).

Modules: power.rs, i18n.rs, config.rs, users.rs, panel.rs, main.rs
45 unit tests passing. Python sources retained as reference.
2026-03-27 16:09:51 +01:00

2.7 KiB

Hekate — Journal

2026-03-27 — Rust Rewrite

Rewrite von Python auf Rust (gtk4-rs + gtk4-layer-shell). Motivation: ~800ms Startzeit der Python-Version durch Interpreter-Overhead.

Alle Module 1:1 portiert:

  • power.rs — Command::new statt subprocess.run, PowerError enum statt Exceptions
  • i18n.rs — Static Strings statt Dataclass, parse_lang_prefix() separat testbar (kein env::set_var nötig)
  • config.rs — serde::Deserialize für TOML, GResource-Pfad als letzter Fallback
  • users.rs — nix-crate für getuid/getpwuid, GResource-Pfad für default-avatar
  • panel.rs — Freie Funktionen statt Klassen, Rc für Confirm-State, glib::spawn_future_local + gio::spawn_blocking für async Power-Actions
  • main.rs — GResource-Registration, LayerShell trait statt Gtk4LayerShell-Modul

45 Unit-Tests grün. Release-Binary: 3.1 MB.

Gelernt:

  • gtk4-rs 0.11 braucht Rust ≥1.92 (system hatte 1.91 → rustup update)
  • ContentFit und Widget::color() brauchen Feature-Flags (v4_8, v4_10)
  • GTK-Objekte (WeakRef) sind nicht Send → glib::spawn_future_local statt std::thread für UI-Updates
  • set_from_paintable heißt jetzt set_paintable in gtk4-rs 0.11
  • GResource-Bundle kompiliert CSS/Wallpaper/Avatar in die Binary — kein importlib.resources mehr nötig

2026-03-27 — Initiale Python-Version

Erster Tag. Moonset von Null auf v0.1.0 gebracht. TDD durchgezogen — alle 54 Tests grün, bevor der erste manuelle Start passiert ist. Das Pattern aus moongreet/moonlock hat sich bewährt: power.py, i18n.py, config.py sind fast 1:1 übernommen, nur mit den 5 Aktionen erweitert.

Layer Shell brauchte LD_PRELOAD — selbes Thema wie bei moongreet. GI-Import allein reicht nicht, weil die Linker-Reihenfolge stimmen muss. Erster Start ohne LD_PRELOAD gab die bekannten Warnings, mit LD_PRELOAD lief alles sauber: Overlay auf allen Monitoren, Escape schließt, Buttons da.

Designentscheidung: Lock ohne Confirmation, alles andere mit Inline-Confirm. Fühlt sich richtig an — Lock ist sofort reversibel, Shutdown nicht.

v0.2.0 direkt hinterher. Viel gelernt:

  • exclusive_zone = -1 ist Pflicht, sonst respektiert man Waybars Zone
  • Monitor-Detection über is_primary() ist unzuverlässig auf Niri — stattdessen kein set_monitor() und den Compositor entscheiden lassen
  • Icon-Theme-Lookup: 22px-Variante laden und per GdkPixbuf auf 64px skalieren, damit die gleichen Icons wie bei moonlock erscheinen
  • CSS Fade-In Animationen auf Layer Shell Surfaces wirken ruckelig (wenige FPS) — rausgenommen
  • loginctl lock-session braucht einen D-Bus-Listener der schwer aufzusetzen ist — moonlock direkt aufrufen ist einfacher und zuverlässiger
  • LD_PRELOAD über den Niri-Keybind setzen spart den Reexec und damit ~1s Startzeit