self: { config, lib, pkgs, ... }: let cfg = config.services.rhemawave; in { options.services.rhemawave = { enable = lib.mkEnableOption "RhemaWave kiosk application"; package = lib.mkOption { type = lib.types.package; default = self.packages.${pkgs.system}.default; description = "The RhemaWave package to use."; }; user = lib.mkOption { type = lib.types.str; default = "kiosk"; description = "User account that runs the kiosk session."; }; autoVolume = lib.mkOption { type = lib.types.bool; default = true; description = "Set all PipeWire sinks to full volume before launching."; }; }; config = lib.mkIf cfg.enable { # --- 1. Environment & Desktop setup --- environment.sessionVariables = { SDL_VIDEODRIVER = "wayland"; QT_QPA_PLATFORM = "wayland"; GDK_BACKEND = "wayland"; CLUTTER_BACKEND = "wayland"; _JAVA_AWT_WM_NONREPARENTING = "1"; }; # --- 2. Asset Management & Runtime Dirs --- systemd.tmpfiles.rules = [ # User Runtime Directory for Wayland "d /run/user/1000 0700 ${cfg.user} kiosk -" # Asset directory for the kiosk user "d /home/${cfg.user}/.local/share/rhemawave 0700 ${cfg.user} kiosk -" # Symlinks for assets "L+ /home/${cfg.user}/.local/share/rhemawave/shaders - - - ${cfg.package}/share/rhemawave/shaders" "L+ /home/${cfg.user}/.local/share/rhemawave/fonts - - - ${cfg.package}/share/rhemawave/fonts" "L+ /home/${cfg.user}/.local/share/rhemawave/tos - - - ${cfg.package}/share/rhemawave/tos" "L+ /home/${cfg.user}/.local/share/rhemawave/audio - - - ${cfg.package}/share/rhemawave/audio" "L+ /home/${cfg.user}/.local/share/rhemawave/proclamations - - - ${cfg.package}/share/rhemawave/proclamations" ]; # --- 3. The Kiosk Service --- systemd.services."getty@tty1".enable = false; systemd.services.rhemawave-kiosk = { description = "RhemaWave Kiosk Cage Session"; after = [ "network.target" "systemd-user-sessions.service" "seatd.service" ]; wants = [ "seatd.service" ]; conflicts = [ "getty@tty1.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "simple"; StandardInput = "tty"; StandardOutput = "journal"; StandardError = "journal"; TTYPath = "/dev/tty1"; UtmpIdentifier = "tty1"; TTYReset = "yes"; TTYVHangup = "yes"; TTYVTDisallocate = "yes"; ExecStart = pkgs.writeShellScript "launch-rhemawave" '' # Take over the physical terminal ${pkgs.util-linux}/bin/chvt 1 || true # Detect GPU if [ -e /dev/dri/card1 ]; then export WLR_DRM_DEVICES=/dev/dri/card1 else export WLR_DRM_DEVICES=/dev/dri/card0 fi export XDG_RUNTIME_DIR="/run/user/1000" export LIBSEAT_BACKEND=seatd export WLR_LIBINPUT_NO_DEVICES=1 # Audio Volume Setup ${lib.optionalString cfg.autoVolume '' ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 1.0 2>/dev/null || true ''} # Launch Cage with XWayland disabled exec ${pkgs.util-linux}/bin/runuser -u ${cfg.user} -- \ ${pkgs.cage}/bin/cage -d -s -- ${cfg.package}/bin/rhemawave ''; Restart = "always"; RestartSec = "3s"; }; }; # --- 4. System Services & Hardware --- networking.networkmanager.enable = true; services.seatd.enable = true; services.pipewire = { enable = true; alsa.enable = true; pulse.enable = true; wireplumber.enable = true; }; services.udev.extraRules = '' SUBSYSTEM=="drm", KERNEL=="card[0-9]*", GROUP="video", MODE="0660" SUBSYSTEM=="input", KERNEL=="event[0-9]*", GROUP="input", MODE="0660" ''; # --- 5. Polkit rules --- security.polkit.extraConfig = '' polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units") { var unit = action.lookup("unit"); if (unit == "rhemawave-update.service" || unit == "rhemawave-kiosk.service") { return polkit.Result.YES; } } }); ''; environment.systemPackages = [ cfg.package pkgs.cage ]; }; }