{config, pkgs, lib, ...}: { # === Wayland + Hyprland === programs.hyprland = { enable = true; xwayland.enable = true; }; # Session variables environment.sessionVariables.NIXOS_OZONE_WL = "1"; # === Lid close behavior === # Don't suspend when lid closes — external display stays active services.logind = { lidSwitch = "ignore"; lidSwitchExternalPower = "ignore"; lidSwitchDocked = "ignore"; }; # Fallback ACPI handler for lid events outside Hyprland session # Primary handling is done via Hyprland bindl in hyprland.conf services.acpid = { enable = true; handlers.lid = { event = "button/lid.*"; action = '' ${pkgs.bash}/bin/bash -c ' LID_STATE=$(cat /proc/acpi/button/lid/LID0/state 2>/dev/null || cat /proc/acpi/button/lid/LID/state 2>/dev/null || echo "open") if echo "$LID_STATE" | grep -qi "closed"; then EVENT="close" else EVENT="open" fi for user_dir in /run/user/*/hypr/; do [ -d "$user_dir" ] || continue SIG=$(ls "$user_dir" 2>/dev/null | head -1) [ -z "$SIG" ] && continue UID=$(basename $(dirname "$user_dir")) USER=$(id -nu $UID 2>/dev/null || echo "sscoble") HOME_DIR=$(getent passwd "$USER" | cut -d: -f6) USER_SCRIPT="$HOME_DIR/.config/hypr/scripts/lid-handler.sh" if [ -x "$USER_SCRIPT" ]; then sudo -u "$USER" \ XDG_RUNTIME_DIR="/run/user/$UID" \ HYPRLAND_INSTANCE_SIGNATURE="$SIG" \ "$USER_SCRIPT" "$EVENT" fi break done ' ''; }; }; # Security — required for hyprlock security.pam.services.hyprlock = {}; # Polkit for GUI auth prompts security.polkit.enable = true; systemd.user.services.polkit-gnome-authentication-agent-1 = { description = "polkit-gnome-authentication-agent-1"; wantedBy = [ "graphical-session.target" ]; wants = [ "graphical-session.target" ]; after = [ "graphical-session.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; Restart = "on-failure"; RestartSec = 1; }; }; # Display manager — greetd (minimal, Wayland-native) services.greetd = { enable = true; settings = { default_session = { command = "${pkgs.tuigreet}/bin/tuigreet --time --remember --cmd start-hyprland"; user = "greeter"; }; }; }; # Clear TTY before greetd starts to avoid boot message overlap systemd.services.greetd.serviceConfig = { TTYVTDisallocate = true; Type = "idle"; }; # Create greeter user for greetd users.users.greeter = { description = "Greeter"; group = "greeter"; isSystemUser = true; }; users.groups.greeter = {}; # Suppress kernel boot messages on console boot.consoleLogLevel = 0; # XDG portal for Wayland xdg.portal = { enable = true; extraPortals = [ pkgs.xdg-desktop-portal-hyprland ]; config.common.default = "*"; }; # Fonts fonts.packages = with pkgs; [ font-awesome nerd-fonts.jetbrains-mono nerd-fonts.fira-code ]; # Audio (pipewire) services.pipewire = { enable = true; alsa.enable = true; alsa.support32Bit = true; pulse.enable = true; }; # Bluetooth hardware.bluetooth.enable = true; hardware.bluetooth.powerOnBoot = true; # Bluetooth manager (GUI pairing, tray applet) # Note: using manual setup instead of services.blueman.enable to avoid # a systemd unit bug (duplicate ExecStart in blueman-applet.service). # The applet is launched via exec-once in hyprland.conf instead. environment.systemPackages = with pkgs; [ blueman ]; services.dbus.packages = with pkgs; [ blueman ]; systemd.user.services.blueman-applet = { enable = false; }; systemd.user.services.blueman-manager = { enable = false; }; # NetworkManager applet programs.nm-applet.enable = true; }