# settings/hyprland.nix { config, pkgs, lib, inputs, ... }: { config = lib.mkIf (config.networking.hostName == "FredOS-Gaming") { programs.hyprland = { enable = true; xwayland.enable = true; # Pin to upstream Hyprland flake — 25.11's nixpkgs only has 0.52.1 # and the resize-lag fix landed in 0.52.2. package = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.hyprland; portalPackage = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.xdg-desktop-portal-hyprland; }; xdg.portal = { enable = true; # xdg-desktop-portal-hyprland is registered automatically by # programs.hyprland.portalPackage; listing it here too produced a # duplicate user-unit symlink during nixos-rebuild. extraPortals = with pkgs; [ xdg-desktop-portal-gtk ]; config.hyprland.default = [ "hyprland" "gtk" ]; }; security.polkit.enable = true; # Polkit GUI agent for GUI sudo prompts under Hyprland systemd.user.services.polkit-gnome-authentication-agent-1 = { description = "polkit-gnome-authentication-agent-1"; wantedBy = [ "graphical-session.target" ]; partOf = [ "graphical-session.target" ]; after = [ "graphical-session.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; Restart = "on-failure"; }; }; environment.systemPackages = with pkgs; [ ghostty mako grim slurp wl-clipboard cliphist brightnessctl pamixer playerctl hyprpaper hyprlock hypridle hyprshot networkmanagerapplet pavucontrol polkit_gnome ]; home-manager.users.fred = { config, lib, pkgs, inputs, ... }: { wayland.windowManager.hyprland = { enable = true; systemd.variables = [ "--all" ]; package = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.hyprland; settings = { # Use the EDID-native 180 Hz mode. 190 Hz is technically faster # but the OC mode doesn't advertise FreeSync — at 180 Hz we get # working VRR, which usually beats the +10 Hz on perceived # smoothness. monitor = "DP-2,3440x1440@180,0x0,1"; # FredOS-Gaming has both a Ryzen iGPU (card0, 1002:164E) and a # Navi 22 dGPU (card1, 1002:73DF). Force aquamarine + clients onto # the dGPU so we don't pay PRIME-copy latency every frame. env = [ "AQ_DRM_DEVICES,/dev/dri/card1" "WLR_DRM_DEVICES,/dev/dri/card1" "DRI_PRIME,pci-0000_03_00_0" # Keep these in sync with stylix.cursor in settings/stylix.nix. "XCURSOR_THEME,Bibata-Modern-Ice" "XCURSOR_SIZE,24" "HYPRCURSOR_THEME,Bibata-Modern-Ice" "HYPRCURSOR_SIZE,24" # Prefer native Wayland backends where the app supports it. "ELECTRON_OZONE_PLATFORM_HINT,wayland" # Vesktop, VSCodium "MOZ_ENABLE_WAYLAND,1" # Zen / Firefox family "QT_QPA_PLATFORM,wayland;xcb" # Qt apps, XWayland fallback "SDL_VIDEODRIVER,wayland" # SDL apps "_JAVA_AWT_WM_NONREPARENTING,1" # Java tiling fix ]; "$mod" = "SUPER"; "$term" = "ghostty"; "$menu" = "fuzzel"; exec-once = [ # waybar starts via its systemd user service — see programs.waybar # below — so it waits for graphical-session.target instead of # racing the Wayland init. "mako" "hyprpaper" "nm-applet --indicator" "wl-paste --type text --watch cliphist store" "wl-paste --type image --watch cliphist store" "hyprctl setcursor Bibata-Modern-Ice 24" ]; general = { gaps_in = 6; gaps_out = 12; border_size = 2; layout = "dwindle"; resize_on_border = true; }; decoration = { rounding = 8; # Blur disabled — two passes at 3440x1440 was the biggest # contributor to per-frame compositor latency. Re-enable with # smaller size/passes if you miss the look. blur = { enabled = true; }; }; render = { direct_scanout = true; # fullscreen apps bypass compositor }; animations = { enabled = true; # speed = tenths of a second; 1 = 0.1s ≈ instant. bezier = [ "snap, 0.05, 0.9, 0.1, 1.0" ]; animation = [ "windows, 1, 1, snap" "windowsOut, 1, 1, snap, popin 80%" "border, 1, 2, default" "fade, 1, 1, default" "workspaces, 1, 1, snap" ]; }; input = { kb_layout = "gb,no"; kb_options = "grp:alt_shift_toggle"; follow_mouse = 1; accel_profile = "flat"; sensitivity = 0; }; cursor = { no_warps = true; # don't teleport the cursor on focus changes }; dwindle = { preserve_split = true; # New windows split the focused container 50/50 — your usual # 2-way layout falls out of dwindle's defaults. }; misc = { disable_hyprland_logo = true; disable_splash_rendering = true; # Apps demanding attention don't get to yank focus — they'll # show as urgent in the bar instead. Pairs with cursor.no_warps # below; without that, focus jumps drag the cursor with them. focus_on_activate = false; vrr = 2; # always on — should engage at the 180 Hz EDID mode }; # vfr moved from misc: to debug: in 0.55.0 debug = { vfr = false; # keep compositor ticking, don't idle between frames }; # Mirror the GNOME bindings so muscle memory carries over bind = [ "$mod, T, exec, $term" "$mod, E, exec, nautilus" "$mod, R, exec, $menu" "$mod, Q, killactive" "$mod SHIFT, E, exit" # Floating / layout "$mod, V, togglefloating" "$mod, F, fullscreen, 0" "$mod, P, pseudo" "$mod, S, layoutmsg, togglesplit" # Focus "$mod, left, movefocus, l" "$mod, right, movefocus, r" "$mod, up, movefocus, u" "$mod, down, movefocus, d" "$mod, H, movefocus, l" "$mod, K, movefocus, u" "$mod, J, movefocus, d" # L freed for hyprlock — arrow keys still handle right-focus. # Lock screen "$mod, L, exec, hyprlock" # Move windows "$mod SHIFT, left, movewindow, l" "$mod SHIFT, right, movewindow, r" "$mod SHIFT, up, movewindow, u" "$mod SHIFT, down, movewindow, d" # Workspaces "$mod, 1, workspace, 1" "$mod, 2, workspace, 2" "$mod, 3, workspace, 3" "$mod, 4, workspace, 4" "$mod, 5, workspace, 5" "$mod, 6, workspace, 6" "$mod, 7, workspace, 7" "$mod, 8, workspace, 8" "$mod, 9, workspace, 9" "$mod, 0, workspace, 10" "$mod SHIFT, 1, movetoworkspacesilent, 1" "$mod SHIFT, 2, movetoworkspacesilent, 2" "$mod SHIFT, 3, movetoworkspacesilent, 3" "$mod SHIFT, 4, movetoworkspacesilent, 4" "$mod SHIFT, 5, movetoworkspacesilent, 5" "$mod SHIFT, 6, movetoworkspacesilent, 6" "$mod SHIFT, 7, movetoworkspacesilent, 7" "$mod SHIFT, 8, movetoworkspacesilent, 8" "$mod SHIFT, 9, movetoworkspacesilent, 9" "$mod SHIFT, 0, movetoworkspacesilent, 10" # Screenshots — Shift+Super+S matches your GNOME binding "$mod SHIFT, S, exec, hyprshot -m region --clipboard-only" ", Print, exec, hyprshot -m output --clipboard-only" # Settings shortcut — Super+I matches your GNOME binding "$mod, I, exec, pavucontrol" ]; bindm = [ "$mod, mouse:272, movewindow" "$mod, mouse:273, resizewindow" ]; bindel = [ ", XF86AudioRaiseVolume, exec, pamixer -i 5" ", XF86AudioLowerVolume, exec, pamixer -d 5" ", XF86AudioMute, exec, pamixer -t" ", XF86MonBrightnessUp, exec, brightnessctl set 5%+" ", XF86MonBrightnessDown, exec, brightnessctl set 5%-" ]; bindl = [ ", XF86AudioPlay, exec, playerctl play-pause" ", XF86AudioNext, exec, playerctl next" ", XF86AudioPrev, exec, playerctl previous" ]; }; }; programs.fuzzel = { enable = true; settings = { # Font + colors now come from stylix.targets.fuzzel. main = { lines = 8; width = 64; horizontal-pad = 24 vertical-pad = 24; inner-pad = 16; icon-theme = "Adwaita"; terminal = "ghostty"; prompt = "\" \""; }; border = { width = 1; radius = 10; }; dmenu = { exit-immediately-if-empty = "yes"; }; }; }; programs.waybar = { enable = true; systemd.enable = true; # start via graphical-session.target settings.mainBar = { layer = "top"; position = "top"; height = 30; spacing = 6; modules-left = [ "hyprland/workspaces" ]; modules-center = [ "clock" ]; modules-right = [ "pulseaudio" "network" "tray" "custom/power" ]; "hyprland/workspaces" = { format = "{name}"; on-click = "activate"; sort-by-number = true; }; clock = { format = "{:%H:%M}"; tooltip-format = "{:%A, %d %B %Y}\n{calendar}"; }; pulseaudio = { format = "{icon} {volume}%"; format-muted = " muted"; format-icons = { default = [ "" "" "" ]; headphone = ""; headset = ""; }; on-click = "pavucontrol"; scroll-step = 5; }; network = { format-wifi = "󰖩 {essid}"; format-ethernet = "󰈀"; format-disconnected = "󰖪"; tooltip-format = "{ifname}"; on-click = "nm-connection-editor"; }; tray = { icon-size = 16; spacing = 8; }; "custom/power" = { format = "⏻"; tooltip = false; on-click = "${pkgs.writeShellScript "power-menu" '' choice=$(printf "Lock\nLogout\nReboot\nShutdown" \ | ${pkgs.fuzzel}/bin/fuzzel --dmenu --prompt="Power: " --lines=4 --width=12) case "$choice" in Lock) ${pkgs.hyprlock}/bin/hyprlock ;; Logout) hyprctl dispatch exit ;; Reboot) systemctl reboot ;; Shutdown) systemctl poweroff ;; esac ''}"; }; }; # Colour tokens (@base00..@base0F) are injected by stylix's waybar # target. We reference them instead of hardcoded hex so the bar # follows the wallpaper-derived palette. style = '' * { font-family: "FiraMono Nerd Font", monospace; font-size: 13px; min-height: 0; border: none; border-radius: 0; } window#waybar { background: alpha(@base00, 0.82); color: @base05; } #workspaces { margin-left: 6px; } #workspaces button { padding: 0 8px; color: @base03; background: transparent; } #workspaces button.active { color: @base05; } #workspaces button:hover { background: alpha(@base05, 0.08); color: @base05; box-shadow: none; text-shadow: none; } #clock { color: @base05; font-weight: 500; } #pulseaudio, #network, #tray, #custom-power { padding: 0 10px; color: @base05; } #pulseaudio.muted, #network.disconnected { color: @base03; } #custom-power:hover { color: @base08; } #custom-power { margin-right: 6px; } #tray { margin-right: 0; } ''; }; }; }; }