refactor(hosts): simplify streaming display configuration
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.
This commit is contained in:
@@ -1,25 +1,15 @@
|
||||
# Monitor layout for 14900k.
|
||||
# NVIDIA (default): DP-3 ultrawide, HDMI-A-3 + DP-4 side/top stack; DP-2 virtual 4K when streamingDisplay enabled.
|
||||
# Intel iGPU (gpu passthru): DP-1 ultrawide, HDMI-A-2 Samsung to the left.
|
||||
# Niri: `extraSettings` (KDL via `extraConfig`, other wrapper-modules keys) + `extraBinds` (merged keybinds).
|
||||
# Hyprland: `chiasson.desktop.hyprland.settings` (merged in HM when `chiasson.desktop.hyprland.enable`).
|
||||
# NVIDIA (default): DP-3 ultrawide, HDMI-A-3 + DP-4 stack; DP-2 virtual 4K when streamingDisplay is on.
|
||||
# Intel iGPU (gpu passthru): DP-1 ultrawide, HDMI-A-2 to the left.
|
||||
# Niri uses `extraSettings.extraConfig`; Hyprland uses `chiasson.desktop.hyprland.settings`.
|
||||
|
||||
#TODO[epic=Moderate] Clean this up, move to host's configuration.nix.
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
streamingDisplay = config.chiasson.system.streamingDisplay;
|
||||
gpuPassthru = config.chiasson.system.gpuPassthru.enable;
|
||||
streamingDisplay = config.chiasson.system.streamingDisplay.enable or false;
|
||||
streamingCfg = config.chiasson.system.streamingDisplay or { };
|
||||
|
||||
virtualOutputNiri =
|
||||
lib.optionalString streamingDisplay ''
|
||||
output "${streamingCfg.connector}" {
|
||||
mode "${streamingCfg.mode}"
|
||||
scale ${toString (streamingCfg.niriScale or 1.0)}
|
||||
position ${streamingCfg.niriPosition}
|
||||
}
|
||||
'';
|
||||
|
||||
# Niri outputs: a static block per GPU layout, plus the optional virtual 4K panel.
|
||||
niriOutputs =
|
||||
(if gpuPassthru then
|
||||
''
|
||||
@@ -54,48 +44,30 @@ let
|
||||
position x=0 y=-1080
|
||||
}
|
||||
'')
|
||||
+ virtualOutputNiri;
|
||||
|
||||
virtualOutputHyprland =
|
||||
lib.optionalString streamingDisplay
|
||||
"${streamingCfg.connector}, ${streamingCfg.mode}, 7680x0, ${toString (streamingCfg.niriScale or 1.0)}";
|
||||
+ lib.optionalString streamingDisplay.enable ''
|
||||
output "${streamingDisplay.connector}" {
|
||||
mode "${streamingDisplay.mode}"
|
||||
scale ${toString streamingDisplay.niriScale}
|
||||
position ${streamingDisplay.niriPosition}
|
||||
}
|
||||
'';
|
||||
|
||||
# Hyprland monitor strings, with the virtual panel appended when enabled.
|
||||
hyprlandMonitors =
|
||||
(if gpuPassthru then
|
||||
[
|
||||
"DP-1, 2560x1080@144, 0x0, 1"
|
||||
"HDMI-A-2, 1920x1080@60, -1920x0, 1"
|
||||
]
|
||||
[ "DP-1, 2560x1080@144, 0x0, 1" "HDMI-A-2, 1920x1080@60, -1920x0, 1" ]
|
||||
else
|
||||
[
|
||||
"DP-3, 2560x1080@144, 0x0, 1"
|
||||
"DP-4, 1920x1080@144, 0x-1080, 1"
|
||||
"HDMI-A-3, 1920x1080@60, -1920x0, 1"
|
||||
])
|
||||
++ lib.optional streamingDisplay virtualOutputHyprland;
|
||||
[ "DP-3, 2560x1080@144, 0x0, 1" "DP-4, 1920x1080@144, 0x-1080, 1" "HDMI-A-3, 1920x1080@60, -1920x0, 1" ])
|
||||
++ lib.optional streamingDisplay.enable "${streamingDisplay.connector}, ${streamingDisplay.mode}, 7680x0, ${toString streamingDisplay.niriScale}";
|
||||
|
||||
hyprlandWorkspaces =
|
||||
if gpuPassthru then
|
||||
[
|
||||
"1, monitor:DP-1, default:true"
|
||||
"2, monitor:DP-1"
|
||||
"3, monitor:DP-1"
|
||||
"4, monitor:HDMI-A-2, default:true"
|
||||
"5, monitor:HDMI-A-2"
|
||||
"6, monitor:HDMI-A-2"
|
||||
]
|
||||
[ "1, monitor:DP-1, default:true" "2, monitor:DP-1" "3, monitor:DP-1"
|
||||
"4, monitor:HDMI-A-2, default:true" "5, monitor:HDMI-A-2" "6, monitor:HDMI-A-2" ]
|
||||
else
|
||||
[
|
||||
"1, monitor:DP-3, default:true"
|
||||
"2, monitor:DP-3"
|
||||
"3, monitor:DP-3"
|
||||
"4, monitor:HDMI-A-3, default:true"
|
||||
"5, monitor:HDMI-A-3"
|
||||
"6, monitor:HDMI-A-3"
|
||||
"7, monitor:DP-4"
|
||||
"8, monitor:DP-4"
|
||||
"9, monitor:DP-4"
|
||||
];
|
||||
[ "1, monitor:DP-3, default:true" "2, monitor:DP-3" "3, monitor:DP-3"
|
||||
"4, monitor:HDMI-A-3, default:true" "5, monitor:HDMI-A-3" "6, monitor:HDMI-A-3"
|
||||
"7, monitor:DP-4" "8, monitor:DP-4" "9, monitor:DP-4" ];
|
||||
in
|
||||
{
|
||||
chiasson.desktop.niri.extraSettings = {
|
||||
@@ -103,10 +75,7 @@ in
|
||||
};
|
||||
|
||||
chiasson.desktop.niri.extraBinds."XF86Tools".spawn = [
|
||||
"wpctl"
|
||||
"set-mute"
|
||||
"@DEFAULT_AUDIO_SOURCE@"
|
||||
"toggle"
|
||||
"wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"
|
||||
];
|
||||
|
||||
chiasson.desktop.hyprland.settings = lib.mkIf config.chiasson.desktop.hyprland.enable {
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# Headless 4K virtual output for Sunshine → Moonlight on a client TV.
|
||||
#
|
||||
# NVIDIA proprietary driver: both kernel params are required —
|
||||
# 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
|
||||
#
|
||||
# Pick a connector that is disconnected on the host (check after boot):
|
||||
# grep -H . /sys/class/drm/card*-*/status
|
||||
#
|
||||
# Reboot after changing connector or EDID. 4K@60 is realistic on RTX 2070;
|
||||
# 4K@120 on a sink-less connector needs driver patches (see NVIDIA issue #1184).
|
||||
# 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;
|
||||
@@ -16,14 +14,13 @@ let
|
||||
edidFirmware = pkgs.runCommand "sunshine-virtual-4k-edid" {
|
||||
nativeBuildInputs = [ pkgs.python3 ];
|
||||
} ''
|
||||
${pkgs.python3}/bin/python3 ${./generate-virtual-edid.py} $TMPDIR/${cfg.edidFileName}
|
||||
mkdir -p $out/lib/firmware/edid
|
||||
cp $TMPDIR/${cfg.edidFileName} $out/lib/firmware/edid/${cfg.edidFileName}
|
||||
${pkgs.python3}/bin/python3 ${./generate-virtual-edid.py} $out/lib/firmware/edid/${cfg.edidFileName}
|
||||
'';
|
||||
|
||||
# Sunshine 2025.924 wlr capture only accepts a numeric monitor index (not "DP-2").
|
||||
# If the index drifts after hotplug, check:
|
||||
# journalctl --user -u sunshine -b | rg 'Monitor [0-9]+ is'
|
||||
# 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}
|
||||
@@ -87,18 +84,6 @@ in
|
||||
default = "DP-3";
|
||||
description = "Niri monitor to refocus when a Moonlight session ends.";
|
||||
};
|
||||
|
||||
sunshineOutputIndex = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "3";
|
||||
example = "3";
|
||||
description = ''
|
||||
Wayland/wlr monitor index for Sunshine `output_name`. Sunshine 2025.924 ignores
|
||||
connector strings like `DP-2` and falls back to monitor 0 (your primary).
|
||||
Find the index with:
|
||||
`journalctl --user -u sunshine -b | rg "Monitor [0-9]+ is ${cfg.connector}"`
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
@@ -118,7 +103,11 @@ in
|
||||
(lib.mkIf config.chiasson.system.gaming.sunshine.enable {
|
||||
services.sunshine.settings = {
|
||||
capture = "wlr";
|
||||
output_name = cfg.sunshineOutputIndex;
|
||||
# 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\"}]";
|
||||
};
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user