# 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 fuzzel 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" # Match the GNOME session's cursor — same Adwaita already # available from the gnome.nix package set. "XCURSOR_THEME,Adwaita" "XCURSOR_SIZE,24" "HYPRCURSOR_THEME,Adwaita" "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" "mako" "hyprpaper" "nm-applet --indicator" "wl-paste --type text --watch cliphist store" "wl-paste --type image --watch cliphist store" "hyprctl setcursor Adwaita 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 = false; }; }; 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 }; xwayland = { # Skip Hyprland's compositor-side scaling for XWayland surfaces. # Often helps Wine games recover better from resize/move events. force_zero_scaling = true; }; 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, L, movefocus, r" "$mod, K, movefocus, u" "$mod, J, movefocus, d" # 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, movetoworkspace, 1" "$mod SHIFT, 2, movetoworkspace, 2" "$mod SHIFT, 3, movetoworkspace, 3" "$mod SHIFT, 4, movetoworkspace, 4" "$mod SHIFT, 5, movetoworkspace, 5" "$mod SHIFT, 6, movetoworkspace, 6" "$mod SHIFT, 7, movetoworkspace, 7" "$mod SHIFT, 8, movetoworkspace, 8" "$mod SHIFT, 9, movetoworkspace, 9" "$mod SHIFT, 0, movetoworkspace, 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" ]; }; }; home.file.".config/hypr/hyprpaper.conf".text = '' preload = ${config.home.homeDirectory}/.local/share/backgrounds/wallpaper.png wallpaper = ,${config.home.homeDirectory}/.local/share/backgrounds/wallpaper.png splash = false ''; programs.waybar = { enable = true; settings.mainBar = { layer = "top"; position = "top"; height = 30; spacing = 6; modules-left = [ "hyprland/workspaces" ]; modules-center = [ "clock" ]; modules-right = [ "pulseaudio" "network" "tray" ]; "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; }; }; style = '' * { font-family: "FiraMono Nerd Font", monospace; font-size: 13px; min-height: 0; border: none; border-radius: 0; } window#waybar { background: rgba(20, 20, 20, 0.82); color: #e8e8e8; } #workspaces { margin-left: 6px; } #workspaces button { padding: 0 8px; color: #6e6e6e; background: transparent; } #workspaces button.active { color: #ffffff; } #workspaces button:hover { background: rgba(255, 255, 255, 0.06); color: #ffffff; box-shadow: none; text-shadow: none; } #clock { color: #ffffff; font-weight: 500; } #pulseaudio, #network, #tray { padding: 0 10px; color: #e8e8e8; } #pulseaudio.muted, #network.disconnected { color: #6e6e6e; } #tray { margin-right: 6px; } ''; }; }; }; }