# settings/hyprland.nix { config, pkgs, lib, inputs, ... }: let hyprland-pkgs = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}; # hyprutils-0.13.1 requires GLIBCXX_3.4.34 (GCC 15) but Hyprland's RPATH # was patched to gcc-14.3.0-lib which only provides up to GLIBCXX_3.4.33. # Use our nixpkgs GCC 15 to supply the missing symbol via LD_PRELOAD. gcc15-stdlib = pkgs.gcc15.cc.lib; hyprland-wrapped = pkgs.symlinkJoin { name = hyprland-pkgs.hyprland.name; version = hyprland-pkgs.hyprland.version; paths = [ hyprland-pkgs.hyprland ]; nativeBuildInputs = [ pkgs.makeWrapper ]; # passthru inside symlinkJoin flows through runCommand → mkDerivation, # which correctly sets drv.passthru. The display manager requires # providedSessions; HM's hyprland module requires override. passthru = { providedSessions = [ "hyprland" ]; override = _: hyprland-wrapped; }; postBuild = '' wrapProgram $out/bin/start-hyprland \ --set LD_PRELOAD "${gcc15-stdlib}/lib/libstdc++.so.6" wrapProgram $out/bin/Hyprland \ --set LD_PRELOAD "${gcc15-stdlib}/lib/libstdc++.so.6" # The session .desktop symlinks to the original package and contains # its absolute store path. Rewrite Exec= to our wrapped binary so # GDM actually launches the LD_PRELOAD wrapper, not the bare binary. desktop=$out/share/wayland-sessions/hyprland.desktop orig=$(readlink -f "$desktop") rm "$desktop" sed "s|^Exec=.*|Exec=$out/bin/start-hyprland|" "$orig" > "$desktop" ''; }; in { config = lib.mkIf (config.networking.hostName == "FredOS-Gaming") { programs.hyprland = { enable = true; xwayland.enable = true; package = hyprland-wrapped; portalPackage = hyprland-pkgs.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 = hyprland-wrapped; settings = { monitor = "DP-2,3440x1440@180,0x0,1"; 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 ]; layerrule = "noanim on, ^(anyrun)$"; "$mod" = "SUPER"; "$term" = "ghostty"; "$menu" = "killall anyrun || anyrun"; exec-once = [ "mako" "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 = { enabled = true; }; }; render = { direct_scanout = false; }; 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 disable_logs = false; }; # 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.anyrun = { enable = true; config = { plugins = [ "${pkgs.anyrun}/lib/libapplications.so" ]; x.fraction = 0.5; y.fraction = 0.5; width.fraction = 0.3; height.absolute = 0; margin = 16; hideIcons = false; ignoreExclusiveZones = false; layer = "overlay"; hidePluginInfo = true; closeOnClick = true; maxEntries = 8; }; extraCss = let c = config.lib.stylix.colors; in '' * { all: unset; font-family: "FiraMono Nerd Font", monospace; font-size: 13px; } window { background: transparent; } box.main { background: #${c.base00}; border: 1px solid #${c.base03}; border-radius: 10px; padding: 8px; margin: 16px; } text { background: #${c.base01}; color: #${c.base05}; caret-color: #${c.base0D}; padding: 8px 16px; border-radius: 6px; min-height: 0; } list.plugin { background: transparent; } .matches { background: transparent; } .match { padding: 4px 16px; border-radius: 6px; color: #${c.base05}; background: transparent; } .match:selected { background: #${c.base02}; border: none; } label.match.description { color: #${c.base04}; font-size: 11px; } ''; extraConfigFiles."applications.ron".text = '' Config( desktop_actions: false, max_entries: Some(8), terminal: Some("ghostty"), ) ''; }; # Scope all HM Wayland services (hyprpaper, waybar, …) to the # Hyprland session so they don't crash-loop in a GNOME session. wayland.systemd.target = "hyprland-session.target"; programs.waybar = { enable = true; systemd.enable = true; settings.mainBar = { layer = "top"; position = "top"; height = 30; spacing = 6; modules-left = [ "hyprland/workspaces" ]; modules-center = [ "clock" ]; modules-right = [ "group/tray-drawer" "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}"; }; "group/tray-drawer" = { orientation = "horizontal"; drawer = { transition-duration = 500; transition-left-to-right = false; }; modules = [ "custom/tray-handle" "pulseaudio" "tray" ]; }; "custom/tray-handle" = { format = ""; # Nerd Font arrow tooltip = false; }; # Pulseaudio module, now conditionally visible pulseaudio = { format = "{icon} {volume}%"; format-muted = " muted"; format-icons = { default = [ "" "" "" ]; headphone = ""; headset = ""; }; on-click = "pavucontrol"; scroll-step = 5; }; # Tray module, now conditionally visible tray = { icon-size = 16; spacing = 8; }; "custom/power" = { format = "⏻"; tooltip = false; on-click = "${pkgs.writeShellScript "power-menu" '' choice=$(printf " Lock\n Logout\n Reboot\n Shutdown" \ | ${pkgs.anyrun}/bin/anyrun \ --plugins "${pkgs.anyrun}/lib/libstdin.so" \ --show-results-immediately true \ --hide-plugin-info true \ --close-on-click true) case "$choice" in *Lock) ${pkgs.hyprlock}/bin/hyprlock ;; *Logout) hyprctl dispatch exit ;; *Reboot) systemctl reboot ;; *Shutdown) systemctl poweroff ;; esac ''}"; }; }; 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, #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; } #custom-tray-toggle { padding: 0 0px; color: @base05; } ''; }; }; }; }