f1bd5146a3
Refactor the streaming display logic for the 14900k host to use the `streamingDisplay` configuration object directly instead of separate variables. This simplifies the monitor layout logic for both Niri and Hyprland. Additionally, remove the fragile `sunshineOutputIndex` option in favor of using the stable connector name for Sunshine's `output_name`. This prevents issues where numeric monitor indices drift after hotplugging. The change also includes an explicit encoder list for Sunshine to avoid probing for AV1 on hardware that does not support it.
116 lines
4.1 KiB
Nix
116 lines
4.1 KiB
Nix
# Headless 4K virtual output for Sunshine → Moonlight on a client TV.
|
|
#
|
|
# NVIDIA: force-enable a spare DRM connector with a custom EDID via kernel params:
|
|
# video=<connector>:e force-enables the connector
|
|
# drm.edid_firmware=<connector>:… loads modes from the EDID blob
|
|
#
|
|
# Capture is pinned to the connector by name (Sunshine `output_name`), which is
|
|
# stable across reboots/hotplug — unlike a numeric monitor index.
|
|
# Reboot after changing connector or EDID. 4K@60 is realistic on RTX 2070.
|
|
{ config, lib, pkgs, ... }:
|
|
let
|
|
cfg = config.chiasson.system.streamingDisplay;
|
|
|
|
edidFirmware = pkgs.runCommand "sunshine-virtual-4k-edid" {
|
|
nativeBuildInputs = [ pkgs.python3 ];
|
|
} ''
|
|
mkdir -p $out/lib/firmware/edid
|
|
${pkgs.python3}/bin/python3 ${./generate-virtual-edid.py} $out/lib/firmware/edid/${cfg.edidFileName}
|
|
'';
|
|
|
|
# On stream start: scale + focus the virtual 4K output so streamed windows
|
|
# land there. Capture is pinned to the connector via Sunshine `output_name`,
|
|
# so no fragile numeric-index detection is needed.
|
|
streamDisplayUp = pkgs.writeShellScriptBin "stream-display-up" ''
|
|
${pkgs.niri}/bin/niri msg output ${cfg.connector} scale ${toString cfg.niriScale}
|
|
${pkgs.niri}/bin/niri msg action focus-monitor ${cfg.connector}
|
|
${pkgs.niri}/bin/niri msg action move-window-to-monitor ${cfg.connector} || true
|
|
'';
|
|
|
|
streamDisplayDown = pkgs.writeShellScriptBin "stream-display-down" ''
|
|
${pkgs.niri}/bin/niri msg action focus-monitor ${cfg.focusMonitorOnEnd}
|
|
'';
|
|
in
|
|
{
|
|
options.chiasson.system.streamingDisplay = {
|
|
enable = lib.mkEnableOption ''
|
|
Force-enable a spare NVIDIA DRM connector with a 4K EDID for Sunshine/Moonlight.
|
|
Requires reboot. Pair with `chiasson.desktop.niri` output config in `displays.nix`.
|
|
'';
|
|
|
|
connector = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "DP-2";
|
|
example = "HDMI-A-1";
|
|
description = ''
|
|
DRM connector name (e.g. `DP-2`, `HDMI-A-1`). Must be unused on the host —
|
|
14900k desk uses DP-3, DP-4, and HDMI-A-3 by default.
|
|
'';
|
|
};
|
|
|
|
edidFileName = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "virtual-4k.bin";
|
|
description = "Filename under `/lib/firmware/edid/` referenced by `drm.edid_firmware`.";
|
|
};
|
|
|
|
mode = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "3840x2160@60";
|
|
description = "Mode Niri should use on the virtual output (must match EDID).";
|
|
};
|
|
|
|
niriPosition = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "x=7680 y=0";
|
|
description = ''
|
|
Niri `position` for the virtual panel — keep it clear of desk monitors
|
|
so streamed windows do not overlap your ultrawide layout.
|
|
'';
|
|
};
|
|
|
|
niriScale = lib.mkOption {
|
|
type = lib.types.float;
|
|
default = 1.5;
|
|
example = 1.5;
|
|
description = ''
|
|
Niri `scale` on the virtual panel. Values above 1.0 enlarge UI and text
|
|
on the 4K framebuffer (helpful when viewing on a TV from the couch).
|
|
'';
|
|
};
|
|
|
|
focusMonitorOnEnd = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "DP-3";
|
|
description = "Niri monitor to refocus when a Moonlight session ends.";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable (lib.mkMerge [
|
|
{
|
|
hardware.firmware = [ edidFirmware ];
|
|
|
|
boot.kernelParams = [
|
|
"video=${cfg.connector}:e"
|
|
"drm.edid_firmware=${cfg.connector}:edid/${cfg.edidFileName}"
|
|
];
|
|
|
|
environment.systemPackages = [
|
|
streamDisplayUp
|
|
streamDisplayDown
|
|
];
|
|
}
|
|
(lib.mkIf config.chiasson.system.gaming.sunshine.enable {
|
|
services.sunshine.settings = {
|
|
capture = "wlr";
|
|
# Pin capture to the virtual display by connector name (stable across
|
|
# reboots/hotplug). A numeric index drifted to the ultrawide before.
|
|
output_name = cfg.connector;
|
|
# RTX 2070 has no AV1 NVENC; avoid probing av1_nvenc.
|
|
encoder = "hevc_nvenc,h264_nvenc";
|
|
global_prep_cmd = "[{\"do\":\"${streamDisplayUp}/bin/stream-display-up\",\"undo\":\"${streamDisplayDown}/bin/stream-display-down\"}]";
|
|
};
|
|
})
|
|
]);
|
|
}
|