Enhance 14900k configuration with streaming display support and virtual EDID generation
- Added a new `streaming-display.nix` module to configure a headless 4K virtual output for Sunshine and Moonlight. - Introduced a Python script `generate-virtual-edid.py` to create a virtual display EDID with 4K capabilities. - Updated `configuration.nix` to integrate the new streaming display module and enable related services. - Modified `displays.nix` to accommodate the new streaming display settings and ensure compatibility with existing GPU passthrough configurations.
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# Headless 4K virtual output for Sunshine → Moonlight on a client TV.
|
||||
#
|
||||
# NVIDIA proprietary driver: both kernel params are required —
|
||||
# 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).
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.chiasson.system.streamingDisplay;
|
||||
|
||||
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}
|
||||
'';
|
||||
|
||||
# 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'
|
||||
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.";
|
||||
};
|
||||
|
||||
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 [
|
||||
{
|
||||
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";
|
||||
output_name = cfg.sunshineOutputIndex;
|
||||
global_prep_cmd = "[{\"do\":\"${streamDisplayUp}/bin/stream-display-up\",\"undo\":\"${streamDisplayDown}/bin/stream-display-down\"}]";
|
||||
};
|
||||
})
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user