laptop-config/modules/hyprland.nix
2026-05-21 11:07:40 -07:00

139 lines
4 KiB
Nix

{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;
}