diff --git a/modules/home/niri/default.nix b/modules/home/niri/default.nix index f06e7b9..6fe14d8 100644 --- a/modules/home/niri/default.nix +++ b/modules/home/niri/default.nix @@ -12,6 +12,20 @@ default = null; description = "Optional absolute or relative path to a host-specific Niri KDL fragment."; }; + + gameLatencyTweaks.enable = lib.mkEnableOption '' + Niri compositor tweaks aimed at reducing input latency for games. + + Enables overlay-plane direct scanout and the VRR cursor-update suppression + flag in the niri `debug {}` block, and appends a `window-rule { ... }` + that flips on per-window VRR for common game/video app-ids (Steam, + gamescope, mpv, vlc) so that on-demand-VRR outputs actually engage. + + Pair with `variable-refresh-rate on-demand=true` set on the output(s) + where games will run; otherwise the window rule has nothing to drive. + See https://github.com/niri-wm/niri/wiki/Configuration:-Outputs#variable-refresh-rate + and https://github.com/niri-wm/niri/wiki/Configuration:-Debug-Options. + ''; }; config = let @@ -40,8 +54,57 @@ then candidate else null; + # Inject game-latency debug flags into the singleton `debug {}` block in + # the base config. KDL rejects duplicate top-level `debug` blocks, so the + # additions can't live in a per-host KDL fragment; substitution here keeps + # it host-scoped via the option while leaving the base file untouched. + # If the base block ever changes, the replace silently no-ops and the + # flags fall back to off — verify with `niri validate` after edits. + baseConfig = builtins.readFile ./config.kdl; + latencyDebugMarker = " honor-xdg-activation-with-invalid-serial\n}"; + latencyDebugReplacement = '' + honor-xdg-activation-with-invalid-serial + + // Game latency tweaks. + // enable-overlay-planes: allow fullscreen surfaces to scan out via + // DRM overlay planes, bypassing composition. Verify with the + // `toggle-debug-tint` bind — directly-scanned-out surfaces are + // NOT tinted green. + // skip-cursor-only-updates-during-vrr: stops mouse motion alone + // from yanking the VRR cadence around in fullscreen games. + enable-overlay-planes + skip-cursor-only-updates-during-vrr + }''; + configWithLatencyDebug = + if niri.gameLatencyTweaks.enable + then + builtins.replaceStrings + [latencyDebugMarker] + [latencyDebugReplacement] + baseConfig + else baseConfig; + + # Per-window VRR opt-in for common game/video clients. Only meaningful on + # outputs configured `variable-refresh-rate on-demand=true`; a no-op + # elsewhere. + latencyWindowRules = '' + + // Game / media VRR opt-in (game-latency tweaks). + // Engages on outputs configured `variable-refresh-rate on-demand=true`. + // Extend `match` lines for additional clients; check app-ids with + // `niri msg pick-window`. + window-rule { + match app-id="^steam_app_" + match app-id="^gamescope$" + match app-id="^mpv$" + match app-id="^vlc$" + variable-refresh-rate true + } + ''; + combinedConfig = - builtins.readFile ./config.kdl + configWithLatencyDebug + + lib.optionalString niri.gameLatencyTweaks.enable latencyWindowRules + lib.optionalString (hostSnippet != null) ("\n\n// Host-specific overrides\n" + builtins.readFile hostSnippet) + lib.optionalString wallpaperEngineEnabled ''