diff --git a/nixos/doc/manual/release-notes/rl-1709.xml b/nixos/doc/manual/release-notes/rl-1709.xml index 7a92f2e8a63..25766439759 100644 --- a/nixos/doc/manual/release-notes/rl-1709.xml +++ b/nixos/doc/manual/release-notes/rl-1709.xml @@ -17,7 +17,29 @@ has the following highlights: A consequence is that UIDs and GIDs are no longer reused. - + + + The module option now + causes the first head specified in this list to be set as the primary + head. Apart from that, it's now possible to also set additional options + by using an attribute set, for example: + +{ services.xserver.xrandrHeads = [ + "HDMI-0" + { + output = "DVI-0"; + primary = true; + monitorConfig = '' + Option "Rotate" "right" + ''; + } + ]; +} + + This will set the DVI-0 output to be the primary head, + even though HDMI-0 is the first head in the list. + + The following new services were added since the last release: diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 262115b39e7..09fcdd0b72b 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -31,18 +31,51 @@ let pkgs.xorg.fontadobe75dpi ]; + xrandrOptions = { + output = mkOption { + type = types.str; + example = "DVI-0"; + description = '' + The output name of the monitor, as shown by + xrandr + 1 + invoked without arguments. + ''; + }; + + primary = mkOption { + type = types.bool; + default = false; + description = '' + Whether this head is treated as the primary monitor, + ''; + }; + + monitorConfig = mkOption { + type = types.lines; + default = ""; + example = '' + DisplaySize 408 306 + Option "DPMS" "false" + ''; + description = '' + Extra lines to append to the Monitor section + verbatim. + ''; + }; + }; # Just enumerate all heads without discarding XRandR output information. xrandrHeads = let - mkHead = num: output: { + mkHead = num: config: { name = "multihead${toString num}"; - inherit output; + inherit config; }; in imap mkHead cfg.xrandrHeads; xrandrDeviceSection = let monitors = flip map xrandrHeads (h: '' - Option "monitor-${h.output}" "${h.name}" + Option "monitor-${h.config.output}" "${h.name}" ''); # First option is indented through the space in the config but any # subsequent options aren't so we need to apply indentation to @@ -62,9 +95,13 @@ let value = '' Section "Monitor" Identifier "${current.name}" + ${optionalString (current.config.primary) '' + Option "Primary" "true" + ''} ${optionalString (previous != []) '' Option "RightOf" "${(head previous).name}" ''} + ${current.config.monitorConfig} EndSection ''; } ++ previous; @@ -329,13 +366,39 @@ in xrandrHeads = mkOption { default = []; - example = [ "HDMI-0" "DVI-0" ]; - type = with types; listOf string; + example = [ + "HDMI-0" + { output = "DVI-0"; primary = true; } + { output = "DVI-1"; monitorConfig = "Option \"Rotate\" \"left\""; } + ]; + type = with types; listOf (coercedTo str (output: { + inherit output; + }) (submodule { options = xrandrOptions; })); + # Set primary to true for the first head if no other has been set + # primary already. + apply = heads: let + hasPrimary = any (x: x.primary) heads; + firstPrimary = head heads // { primary = true; }; + newHeads = singleton firstPrimary ++ tail heads; + in if heads != [] && !hasPrimary then newHeads else heads; description = '' - Simple multiple monitor configuration, just specify a list of XRandR - outputs which will be mapped from left to right in the order of the + Multiple monitor configuration, just specify a list of XRandR + outputs. The individual elements should be either simple strings or + an attribute set of output options. + + If the element is a string, it is denoting the physical output for a + monitor, if it's an attribute set, you must at least provide the + option. + + The monitors will be mapped from left to right in the order of the list. + By default, the first monitor will be set as the primary monitor if + none of the elements contain an option that has set + to true. + + Only one monitor is allowed to be primary. + Be careful using this option with multiple graphic adapters or with drivers that have poor support for XRandR, unexpected things might happen with those. @@ -469,11 +532,18 @@ in nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; }; - assertions = - [ { assertion = config.security.polkit.enable; - message = "X11 requires Polkit to be enabled (‘security.polkit.enable = true’)."; - } - ]; + assertions = [ + { assertion = config.security.polkit.enable; + message = "X11 requires Polkit to be enabled (‘security.polkit.enable = true’)."; + } + (let primaryHeads = filter (x: x.primary) cfg.xrandrHeads; in { + assertion = length primaryHeads < 2; + message = "Only one head is allowed to be primary in " + + "‘services.xserver.xrandrHeads’, but there are " + + "${toString (length primaryHeads)} heads set to primary: " + + concatMapStringsSep ", " (x: x.output) primaryHeads; + }) + ]; environment.etc = (optionals cfg.exportConfiguration