nixos/xserver: Fix up/refactor xrandrHeads option
Using invalid module options in the submodule isn't very nice, because it doesn't give very useful errors in case of type mismatch, also we don't get descriptions of these options as they're effecively nonexistent to the module system. Another downside of this is that merging of these options isn't done correctly as well (eg. for types.lines). So we now have proper submodules for each xrandrHead and we also use corcedTo in the type of xrandrHeads so that we can populate the submodule's "output" option in case a plain string is defined for a list item. Instead of silently skipping multiple primary heads, we now have an assertion, which displays a message and aborts configuration evaluation appropriately. Signed-off-by: aszlig <aszlig@redmoonstudios.org>
This commit is contained in:
parent
bb6a5b079f
commit
8266c89b55
@ -31,36 +31,51 @@ let
|
|||||||
pkgs.xorg.fontadobe75dpi
|
pkgs.xorg.fontadobe75dpi
|
||||||
];
|
];
|
||||||
|
|
||||||
|
xrandrOptions = {
|
||||||
|
output = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "DVI-0";
|
||||||
|
description = ''
|
||||||
|
The output name of the monitor, as shown by <citerefentry>
|
||||||
|
<refentrytitle>xrandr</refentrytitle>
|
||||||
|
<manvolnum>1</manvolnum>
|
||||||
|
</citerefentry> 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 <literal>Monitor</literal> section
|
||||||
|
verbatim.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Just enumerate all heads without discarding XRandR output information.
|
# Just enumerate all heads without discarding XRandR output information.
|
||||||
xrandrHeads = (
|
xrandrHeads = let
|
||||||
fold
|
mkHead = num: config: {
|
||||||
(nextHead: { index, alreadyPrimary, processedHeads }:
|
name = "multihead${toString num}";
|
||||||
let
|
inherit config;
|
||||||
processedHead = { name = "multihead${toString index}"; } //
|
};
|
||||||
(if isAttrs nextHead then
|
in imap mkHead cfg.xrandrHeads;
|
||||||
{
|
|
||||||
output = nextHead.output;
|
|
||||||
primary = if alreadyPrimary then false else nextHead.primary || index == 0;
|
|
||||||
monitorConfig = nextHead.monitorConfig;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
output = nextHead;
|
|
||||||
primary = if alreadyPrimary then false else index == 0;
|
|
||||||
monitorConfig = "";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
primariness = if alreadyPrimary then true else processedHead.primary;
|
|
||||||
in
|
|
||||||
{ index = index - 1; alreadyPrimary = primariness; processedHeads = ([ processedHead ] ++ processedHeads); })
|
|
||||||
{ index = (length cfg.xrandrHeads) - 1; alreadyPrimary = false; processedHeads = []; }
|
|
||||||
cfg.xrandrHeads
|
|
||||||
).processedHeads;
|
|
||||||
|
|
||||||
xrandrDeviceSection = let
|
xrandrDeviceSection = let
|
||||||
monitors = flip map xrandrHeads (h: ''
|
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
|
# First option is indented through the space in the config but any
|
||||||
# subsequent options aren't so we need to apply indentation to
|
# subsequent options aren't so we need to apply indentation to
|
||||||
@ -80,13 +95,13 @@ let
|
|||||||
value = ''
|
value = ''
|
||||||
Section "Monitor"
|
Section "Monitor"
|
||||||
Identifier "${current.name}"
|
Identifier "${current.name}"
|
||||||
${optionalString (current.primary) ''
|
${optionalString (current.config.primary) ''
|
||||||
Option "Primary" "true"
|
Option "Primary" "true"
|
||||||
''}
|
''}
|
||||||
${optionalString (previous != []) ''
|
${optionalString (previous != []) ''
|
||||||
Option "RightOf" "${(head previous).name}"
|
Option "RightOf" "${(head previous).name}"
|
||||||
''}
|
''}
|
||||||
${current.monitorConfig}
|
${current.config.monitorConfig}
|
||||||
EndSection
|
EndSection
|
||||||
'';
|
'';
|
||||||
} ++ previous;
|
} ++ previous;
|
||||||
@ -351,27 +366,36 @@ in
|
|||||||
|
|
||||||
xrandrHeads = mkOption {
|
xrandrHeads = mkOption {
|
||||||
default = [];
|
default = [];
|
||||||
example = [ "HDMI-0" { output = "DVI-0"; primary = true; monitorConfig = ""; } ];
|
example = [
|
||||||
type = with types; listOf (either
|
"HDMI-0"
|
||||||
str
|
{ output = "DVI-0"; primary = true; }
|
||||||
(submodule {
|
{ output = "DVI-1"; monitorConfig = "Option \"Rotate\" \"left\""; }
|
||||||
options.output = str;
|
];
|
||||||
options.primary = bool;
|
type = with types; listOf (coercedTo str (output: {
|
||||||
options.monitorConfig = lines;
|
inherit output;
|
||||||
})
|
}) (submodule { options = xrandrOptions; }));
|
||||||
);
|
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 = ''
|
description = ''
|
||||||
Simple multiple monitor configuration, just specify a list of XRandR
|
Multiple monitor configuration, just specify a list of XRandR
|
||||||
outputs as the values of the list. The monitors will be mapped from
|
outputs. The individual elements should be either simple strings or
|
||||||
left to right in the order of the list.
|
an attribute set of output options.
|
||||||
|
|
||||||
By default, the first monitor will be set as the primary monitor.
|
If the element is a string, it is denoting the physical output for a
|
||||||
However instead of a list, you can give an attribute set. That set
|
monitor, if it's an attribute set, you must at least provide the
|
||||||
can contain a primary monitor specification and a custom monitor
|
<option>output</option> option.
|
||||||
configuration section.
|
|
||||||
|
|
||||||
Only one monitor is allowed to be primary. If multiple monitors are
|
The monitors will be mapped from left to right in the order of the
|
||||||
specified as primary, only the last monitor will be primary.
|
list.
|
||||||
|
|
||||||
|
By default, the first monitor will be set as the primary monitor if
|
||||||
|
none of the elements contain an option that has set
|
||||||
|
<option>primary</option> to <literal>true</literal>.
|
||||||
|
|
||||||
|
<note><para>Only one monitor is allowed to be primary.</para></note>
|
||||||
|
|
||||||
Be careful using this option with multiple graphic adapters or with
|
Be careful using this option with multiple graphic adapters or with
|
||||||
drivers that have poor support for XRandR, unexpected things might
|
drivers that have poor support for XRandR, unexpected things might
|
||||||
@ -506,11 +530,18 @@ in
|
|||||||
|
|
||||||
nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; };
|
nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; };
|
||||||
|
|
||||||
assertions =
|
assertions = [
|
||||||
[ { assertion = config.security.polkit.enable;
|
{ assertion = config.security.polkit.enable;
|
||||||
message = "X11 requires Polkit to be enabled (‘security.polkit.enable = true’).";
|
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 =
|
environment.etc =
|
||||||
(optionals cfg.exportConfiguration
|
(optionals cfg.exportConfiguration
|
||||||
|
Loading…
x
Reference in New Issue
Block a user