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