1{ config, lib, utils, pkgs, ... }:
2
3with lib;
4
5let
6
7 # Abbreviations.
8 cfg = config.services.xserver;
9 xorg = pkgs.xorg;
10
11
12 # Map video driver names to driver packages. FIXME: move into card-specific modules.
13 knownVideoDrivers = {
14 # Alias so people can keep using "virtualbox" instead of "vboxvideo".
15 virtualbox = { modules = [ xorg.xf86videovboxvideo ]; driverName = "vboxvideo"; };
16
17 # Alias so that "radeon" uses the xf86-video-ati driver.
18 radeon = { modules = [ xorg.xf86videoati ]; driverName = "ati"; };
19
20 # modesetting does not have a xf86videomodesetting package as it is included in xorgserver
21 modesetting = {};
22 };
23
24 fontsForXServer =
25 config.fonts.packages ++
26 # We don't want these fonts in fonts.conf, because then modern,
27 # fontconfig-based applications will get horrible bitmapped
28 # Helvetica fonts. It's better to get a substitution (like Nimbus
29 # Sans) than that horror. But we do need the Adobe fonts for some
30 # old non-fontconfig applications. (Possibly this could be done
31 # better using a fontconfig rule.)
32 [ pkgs.xorg.fontadobe100dpi
33 pkgs.xorg.fontadobe75dpi
34 ];
35
36 xrandrOptions = {
37 output = mkOption {
38 type = types.str;
39 example = "DVI-0";
40 description = ''
41 The output name of the monitor, as shown by
42 {manpage}`xrandr(1)` invoked without arguments.
43 '';
44 };
45
46 primary = mkOption {
47 type = types.bool;
48 default = false;
49 description = ''
50 Whether this head is treated as the primary monitor,
51 '';
52 };
53
54 monitorConfig = mkOption {
55 type = types.lines;
56 default = "";
57 example = ''
58 DisplaySize 408 306
59 Option "DPMS" "false"
60 '';
61 description = ''
62 Extra lines to append to the `Monitor` section
63 verbatim. Available options are documented in the MONITOR section in
64 {manpage}`xorg.conf(5)`.
65 '';
66 };
67 };
68
69 # Just enumerate all heads without discarding XRandR output information.
70 xrandrHeads = let
71 mkHead = num: config: {
72 name = "multihead${toString num}";
73 inherit config;
74 };
75 in imap1 mkHead cfg.xrandrHeads;
76
77 xrandrDeviceSection = let
78 monitors = forEach xrandrHeads (h: ''
79 Option "monitor-${h.config.output}" "${h.name}"
80 '');
81 in concatStrings monitors;
82
83 # Here we chain every monitor from the left to right, so we have:
84 # m4 right of m3 right of m2 right of m1 .----.----.----.----.
85 # Which will end up in reverse ----------> | m1 | m2 | m3 | m4 |
86 # `----^----^----^----'
87 xrandrMonitorSections = let
88 mkMonitor = previous: current: singleton {
89 inherit (current) name;
90 value = ''
91 Section "Monitor"
92 Identifier "${current.name}"
93 ${optionalString (current.config.primary) ''
94 Option "Primary" "true"
95 ''}
96 ${optionalString (previous != []) ''
97 Option "RightOf" "${(head previous).name}"
98 ''}
99 ${current.config.monitorConfig}
100 EndSection
101 '';
102 } ++ previous;
103 monitors = reverseList (foldl mkMonitor [] xrandrHeads);
104 in concatMapStrings (getAttr "value") monitors;
105
106 configFile = pkgs.runCommand "xserver.conf"
107 { fontpath = optionalString (cfg.fontPath != null)
108 ''FontPath "${cfg.fontPath}"'';
109 inherit (cfg) config;
110 preferLocalBuild = true;
111 }
112 ''
113 echo 'Section "Files"' >> $out
114 echo "$fontpath" >> $out
115
116 for i in ${toString fontsForXServer}; do
117 if test "''${i:0:''${#NIX_STORE}}" == "$NIX_STORE"; then
118 for j in $(find $i -name fonts.dir); do
119 echo " FontPath \"$(dirname $j)\"" >> $out
120 done
121 fi
122 done
123
124 ${concatMapStrings (m: ''
125 echo " ModulePath \"${m}/lib/xorg/modules\"" >> "$out"
126 '') cfg.modules}
127
128 echo '${cfg.filesSection}' >> $out
129 echo 'EndSection' >> $out
130 echo >> $out
131
132 echo "$config" >> $out
133 ''; # */
134
135 prefixStringLines = prefix: str:
136 concatMapStringsSep "\n" (line: prefix + line) (splitString "\n" str);
137
138 indent = prefixStringLines " ";
139
140 # A scalable variant of the X11 "core" cursor
141 #
142 # If not running a fancy desktop environment, the cursor is likely set to
143 # the default `cursor.pcf` bitmap font. This is 17px wide, so it's very
144 # small and almost invisible on 4K displays.
145 fontcursormisc_hidpi = pkgs.xorg.fontxfree86type1.overrideAttrs (old:
146 let
147 # The scaling constant is 230/96: the scalable `left_ptr` glyph at
148 # about 23 points is rendered as 17px, on a 96dpi display.
149 # Note: the XLFD font size is in decipoints.
150 size = 2.39583 * cfg.dpi;
151 sizeString = builtins.head (builtins.split "\\." (toString size));
152 in
153 {
154 postInstall = ''
155 alias='cursor -xfree86-cursor-medium-r-normal--0-${sizeString}-0-0-p-0-adobe-fontspecific'
156 echo "$alias" > $out/lib/X11/fonts/Type1/fonts.alias
157 '';
158 });
159in
160
161{
162
163 imports =
164 [ ./display-managers/default.nix
165 ./window-managers/default.nix
166 ./desktop-managers/default.nix
167 (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ]
168 "See the 16.09 release notes for more information.")
169 (mkRemovedOptionModule
170 [ "services" "xserver" "startDbusSession" ]
171 "The user D-Bus session is now always socket activated and this option can safely be removed.")
172 (mkRemovedOptionModule [ "services" "xserver" "useXFS" ]
173 "Use services.xserver.fontPath instead of useXFS")
174 (mkRemovedOptionModule [ "services" "xserver" "useGlamor" ]
175 "Option services.xserver.useGlamor was removed because it is unnecessary. Drivers that uses Glamor will use it automatically.")
176 (lib.mkRenamedOptionModuleWith {
177 sinceRelease = 2311;
178 from = [ "services" "xserver" "layout" ];
179 to = [ "services" "xserver" "xkb" "layout" ];
180 })
181 (lib.mkRenamedOptionModuleWith {
182 sinceRelease = 2311;
183 from = [ "services" "xserver" "xkbModel" ];
184 to = [ "services" "xserver" "xkb" "model" ];
185 })
186 (lib.mkRenamedOptionModuleWith {
187 sinceRelease = 2311;
188 from = [ "services" "xserver" "xkbOptions" ];
189 to = [ "services" "xserver" "xkb" "options" ];
190 })
191 (lib.mkRenamedOptionModuleWith {
192 sinceRelease = 2311;
193 from = [ "services" "xserver" "xkbVariant" ];
194 to = [ "services" "xserver" "xkb" "variant" ];
195 })
196 (lib.mkRenamedOptionModuleWith {
197 sinceRelease = 2311;
198 from = [ "services" "xserver" "xkbDir" ];
199 to = [ "services" "xserver" "xkb" "dir" ];
200 })
201 ];
202
203
204 ###### interface
205
206 options = {
207
208 services.xserver = {
209
210 enable = mkOption {
211 type = types.bool;
212 default = false;
213 description = ''
214 Whether to enable the X server.
215 '';
216 };
217
218 autorun = mkOption {
219 type = types.bool;
220 default = true;
221 description = ''
222 Whether to start the X server automatically.
223 '';
224 };
225
226 excludePackages = mkOption {
227 default = [];
228 example = literalExpression "[ pkgs.xterm ]";
229 type = types.listOf types.package;
230 description = "Which X11 packages to exclude from the default environment";
231 };
232
233 exportConfiguration = mkOption {
234 type = types.bool;
235 default = false;
236 description = ''
237 Whether to symlink the X server configuration under
238 {file}`/etc/X11/xorg.conf`.
239 '';
240 };
241
242 enableTCP = mkOption {
243 type = types.bool;
244 default = false;
245 description = ''
246 Whether to allow the X server to accept TCP connections.
247 '';
248 };
249
250 autoRepeatDelay = mkOption {
251 type = types.nullOr types.int;
252 default = null;
253 description = ''
254 Sets the autorepeat delay (length of time in milliseconds that a key must be depressed before autorepeat starts).
255 '';
256 };
257
258 autoRepeatInterval = mkOption {
259 type = types.nullOr types.int;
260 default = null;
261 description = ''
262 Sets the autorepeat interval (length of time in milliseconds that should elapse between autorepeat-generated keystrokes).
263 '';
264 };
265
266 inputClassSections = mkOption {
267 type = types.listOf types.lines;
268 default = [];
269 example = literalExpression ''
270 [ '''
271 Identifier "Trackpoint Wheel Emulation"
272 MatchProduct "ThinkPad USB Keyboard with TrackPoint"
273 Option "EmulateWheel" "true"
274 Option "EmulateWheelButton" "2"
275 Option "Emulate3Buttons" "false"
276 '''
277 ]
278 '';
279 description = "Content of additional InputClass sections of the X server configuration file.";
280 };
281
282 modules = mkOption {
283 type = types.listOf types.path;
284 default = [];
285 example = literalExpression "[ pkgs.xf86_input_wacom ]";
286 description = "Packages to be added to the module search path of the X server.";
287 };
288
289 resolutions = mkOption {
290 type = types.listOf types.attrs;
291 default = [];
292 example = [ { x = 1600; y = 1200; } { x = 1024; y = 786; } ];
293 description = ''
294 The screen resolutions for the X server. The first element
295 is the default resolution. If this list is empty, the X
296 server will automatically configure the resolution.
297 '';
298 };
299
300 videoDrivers = mkOption {
301 type = types.listOf types.str;
302 default = [ "modesetting" "fbdev" ];
303 example = [
304 "nvidia"
305 "amdgpu-pro"
306 ];
307 # TODO(@oxij): think how to easily add the rest, like those nvidia things
308 relatedPackages = concatLists
309 (mapAttrsToList (n: v:
310 optional (hasPrefix "xf86video" n) {
311 path = [ "xorg" n ];
312 title = removePrefix "xf86video" n;
313 }) pkgs.xorg);
314 description = ''
315 The names of the video drivers the configuration
316 supports. They will be tried in order until one that
317 supports your card is found.
318 Don't combine those with "incompatible" OpenGL implementations,
319 e.g. free ones (mesa-based) with proprietary ones.
320
321 For unfree "nvidia*", the supported GPU lists are on
322 https://www.nvidia.com/object/unix.html
323 '';
324 };
325
326 videoDriver = mkOption {
327 type = types.nullOr types.str;
328 default = null;
329 example = "i810";
330 description = ''
331 The name of the video driver for your graphics card. This
332 option is obsolete; please set the
333 {option}`services.xserver.videoDrivers` instead.
334 '';
335 };
336
337 drivers = mkOption {
338 type = types.listOf types.attrs;
339 internal = true;
340 description = ''
341 A list of attribute sets specifying drivers to be loaded by
342 the X11 server.
343 '';
344 };
345
346 dpi = mkOption {
347 type = types.nullOr types.int;
348 default = null;
349 description = ''
350 Force global DPI resolution to use for X server. It's recommended to
351 use this only when DPI is detected incorrectly; also consider using
352 `Monitor` section in configuration file instead.
353 '';
354 };
355
356 updateDbusEnvironment = mkOption {
357 type = types.bool;
358 default = false;
359 description = ''
360 Whether to update the DBus activation environment after launching the
361 desktop manager.
362 '';
363 };
364
365 xkb = {
366 layout = mkOption {
367 type = types.str;
368 default = "us";
369 description = ''
370 X keyboard layout, or multiple keyboard layouts separated by commas.
371 '';
372 };
373
374 model = mkOption {
375 type = types.str;
376 default = "pc104";
377 example = "presario";
378 description = ''
379 X keyboard model.
380 '';
381 };
382
383 options = mkOption {
384 type = types.commas;
385 default = "terminate:ctrl_alt_bksp";
386 example = "grp:caps_toggle,grp_led:scroll";
387 description = ''
388 X keyboard options; layout switching goes here.
389 '';
390 };
391
392 variant = mkOption {
393 type = types.str;
394 default = "";
395 example = "colemak";
396 description = ''
397 X keyboard variant.
398 '';
399 };
400
401 dir = mkOption {
402 type = types.path;
403 default = "${pkgs.xkeyboard_config}/etc/X11/xkb";
404 defaultText = literalExpression ''"''${pkgs.xkeyboard_config}/etc/X11/xkb"'';
405 description = ''
406 Path used for -xkbdir xserver parameter.
407 '';
408 };
409 };
410
411 config = mkOption {
412 type = types.lines;
413 description = ''
414 The contents of the configuration file of the X server
415 ({file}`xorg.conf`).
416
417 This option is set by multiple modules, and the configs are
418 concatenated together.
419
420 In Xorg configs the last config entries take precedence,
421 so you may want to use `lib.mkAfter` on this option
422 to override NixOS's defaults.
423 '';
424 };
425
426 filesSection = mkOption {
427 type = types.lines;
428 default = "";
429 example = ''FontPath "/path/to/my/fonts"'';
430 description = "Contents of the first `Files` section of the X server configuration file.";
431 };
432
433 deviceSection = mkOption {
434 type = types.lines;
435 default = "";
436 example = "VideoRAM 131072";
437 description = "Contents of the first Device section of the X server configuration file.";
438 };
439
440 screenSection = mkOption {
441 type = types.lines;
442 default = "";
443 example = ''
444 Option "RandRRotation" "on"
445 '';
446 description = "Contents of the first Screen section of the X server configuration file.";
447 };
448
449 monitorSection = mkOption {
450 type = types.lines;
451 default = "";
452 example = "HorizSync 28-49";
453 description = "Contents of the first Monitor section of the X server configuration file.";
454 };
455
456 extraConfig = mkOption {
457 type = types.lines;
458 default = "";
459 description = "Additional contents (sections) included in the X server configuration file";
460 };
461
462 xrandrHeads = mkOption {
463 default = [];
464 example = [
465 "HDMI-0"
466 { output = "DVI-0"; primary = true; }
467 { output = "DVI-1"; monitorConfig = "Option \"Rotate\" \"left\""; }
468 ];
469 type = with types; listOf (coercedTo str (output: {
470 inherit output;
471 }) (submodule { options = xrandrOptions; }));
472 # Set primary to true for the first head if no other has been set
473 # primary already.
474 apply = heads: let
475 hasPrimary = any (x: x.primary) heads;
476 firstPrimary = head heads // { primary = true; };
477 newHeads = singleton firstPrimary ++ tail heads;
478 in if heads != [] && !hasPrimary then newHeads else heads;
479 description = ''
480 Multiple monitor configuration, just specify a list of XRandR
481 outputs. The individual elements should be either simple strings or
482 an attribute set of output options.
483
484 If the element is a string, it is denoting the physical output for a
485 monitor, if it's an attribute set, you must at least provide the
486 {option}`output` option.
487
488 The monitors will be mapped from left to right in the order of the
489 list.
490
491 By default, the first monitor will be set as the primary monitor if
492 none of the elements contain an option that has set
493 {option}`primary` to `true`.
494
495 ::: {.note}
496 Only one monitor is allowed to be primary.
497 :::
498
499 Be careful using this option with multiple graphic adapters or with
500 drivers that have poor support for XRandR, unexpected things might
501 happen with those.
502 '';
503 };
504
505 serverFlagsSection = mkOption {
506 default = "";
507 type = types.lines;
508 example =
509 ''
510 Option "BlankTime" "0"
511 Option "StandbyTime" "0"
512 Option "SuspendTime" "0"
513 Option "OffTime" "0"
514 '';
515 description = "Contents of the ServerFlags section of the X server configuration file.";
516 };
517
518 moduleSection = mkOption {
519 type = types.lines;
520 default = "";
521 example =
522 ''
523 SubSection "extmod"
524 EndSubsection
525 '';
526 description = "Contents of the Module section of the X server configuration file.";
527 };
528
529 serverLayoutSection = mkOption {
530 type = types.lines;
531 default = "";
532 example =
533 ''
534 Option "AIGLX" "true"
535 '';
536 description = "Contents of the ServerLayout section of the X server configuration file.";
537 };
538
539 extraDisplaySettings = mkOption {
540 type = types.lines;
541 default = "";
542 example = "Virtual 2048 2048";
543 description = "Lines to be added to every Display subsection of the Screen section.";
544 };
545
546 defaultDepth = mkOption {
547 type = types.int;
548 default = 0;
549 example = 8;
550 description = "Default colour depth.";
551 };
552
553 fontPath = mkOption {
554 type = types.nullOr types.str;
555 default = null;
556 example = "unix/:7100";
557 description = ''
558 Set the X server FontPath. Defaults to null, which
559 means the compiled in defaults will be used. See
560 man xorg.conf for details.
561 '';
562 };
563
564 tty = mkOption {
565 type = types.nullOr types.int;
566 default = 7;
567 description = "Virtual console for the X server.";
568 };
569
570 display = mkOption {
571 type = types.nullOr types.int;
572 default = 0;
573 description = "Display number for the X server.";
574 };
575
576 virtualScreen = mkOption {
577 type = types.nullOr types.attrs;
578 default = null;
579 example = { x = 2048; y = 2048; };
580 description = ''
581 Virtual screen size for Xrandr.
582 '';
583 };
584
585 logFile = mkOption {
586 type = types.nullOr types.str;
587 default = "/dev/null";
588 example = "/var/log/Xorg.0.log";
589 description = ''
590 Controls the file Xorg logs to.
591
592 The default of `/dev/null` is set so that systemd services (like `displayManagers`) only log to the journal and don't create their own log files.
593
594 Setting this to `null` will not pass the `-logfile` argument to Xorg which allows it to log to its default logfile locations instead (see `man Xorg`). You probably only want this behaviour when running Xorg manually (e.g. via `startx`).
595 '';
596 };
597
598 verbose = mkOption {
599 type = types.nullOr types.int;
600 default = 3;
601 example = 7;
602 description = ''
603 Controls verbosity of X logging.
604 '';
605 };
606
607 enableCtrlAltBackspace = mkOption {
608 type = types.bool;
609 default = false;
610 description = ''
611 Whether to enable the DontZap option, which binds Ctrl+Alt+Backspace
612 to forcefully kill X. This can lead to data loss and is disabled
613 by default.
614 '';
615 };
616
617 terminateOnReset = mkOption {
618 type = types.bool;
619 default = true;
620 description = ''
621 Whether to terminate X upon server reset.
622 '';
623 };
624
625 upscaleDefaultCursor = mkOption {
626 type = types.bool;
627 default = false;
628 description = ''
629 Upscale the default X cursor to be more visible on high-density displays.
630 Requires `config.services.xserver.dpi` to be set.
631 '';
632 };
633 };
634
635 };
636
637
638
639 ###### implementation
640
641 config = mkIf cfg.enable {
642 services.displayManager.enable = true;
643
644 services.xserver.displayManager.lightdm.enable =
645 let dmConf = cfg.displayManager;
646 default = !(dmConf.gdm.enable
647 || config.services.displayManager.sddm.enable
648 || dmConf.xpra.enable
649 || dmConf.sx.enable
650 || dmConf.startx.enable
651 || config.services.greetd.enable);
652 in mkIf (default) (mkDefault true);
653
654 services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];
655
656 # FIXME: somehow check for unknown driver names.
657 services.xserver.drivers = flip concatMap cfg.videoDrivers (name:
658 let driver =
659 attrByPath [name]
660 (if xorg ? ${"xf86video" + name}
661 then { modules = [xorg.${"xf86video" + name}]; }
662 else null)
663 knownVideoDrivers;
664 in optional (driver != null) ({ inherit name; modules = []; driverName = name; display = true; } // driver));
665
666 assertions = [
667 (let primaryHeads = filter (x: x.primary) cfg.xrandrHeads; in {
668 assertion = length primaryHeads < 2;
669 message = "Only one head is allowed to be primary in "
670 + "‘services.xserver.xrandrHeads’, but there are "
671 + "${toString (length primaryHeads)} heads set to primary: "
672 + concatMapStringsSep ", " (x: x.output) primaryHeads;
673 })
674 {
675 assertion = cfg.upscaleDefaultCursor -> cfg.dpi != null;
676 message = "Specify `config.services.xserver.dpi` to upscale the default cursor.";
677 }
678 ];
679
680 environment.etc =
681 (optionalAttrs cfg.exportConfiguration
682 {
683 "X11/xorg.conf".source = "${configFile}";
684 # -xkbdir command line option does not seems to be passed to xkbcomp.
685 "X11/xkb".source = "${cfg.xkb.dir}";
686 })
687 # Needed since 1.18; see https://bugs.freedesktop.org/show_bug.cgi?id=89023#c5
688 // (let cfgPath = "X11/xorg.conf.d/10-evdev.conf"; in
689 {
690 ${cfgPath}.source = xorg.xf86inputevdev.out + "/share/" + cfgPath;
691 });
692
693 environment.systemPackages = utils.removePackagesByName
694 [ xorg.xorgserver.out
695 xorg.xrandr
696 xorg.xrdb
697 xorg.setxkbmap
698 xorg.iceauth # required for KDE applications (it's called by dcopserver)
699 xorg.xlsclients
700 xorg.xset
701 xorg.xsetroot
702 xorg.xinput
703 xorg.xprop
704 xorg.xauth
705 pkgs.xterm
706 xorg.xf86inputevdev.out # get evdev.4 man page
707 ] config.services.xserver.excludePackages
708 ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh;
709
710 environment.pathsToLink = [ "/share/X11" ];
711
712 systemd.services.display-manager =
713 { description = "Display Manager";
714
715 after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
716
717 restartIfChanged = false;
718
719 environment =
720 optionalAttrs config.hardware.opengl.setLdLibraryPath
721 { LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ]; }
722 // config.services.displayManager.environment;
723
724 preStart =
725 ''
726 ${config.services.displayManager.preStart}
727
728 rm -f /tmp/.X0-lock
729 '';
730
731 # Stop restarting if the display manager stops (crashes) 2 times
732 # in one minute. Starting X typically takes 3-4s.
733 startLimitIntervalSec = 30;
734 startLimitBurst = 3;
735 serviceConfig = {
736 Restart = "always";
737 RestartSec = "200ms";
738 SyslogIdentifier = "display-manager";
739 };
740 };
741
742 services.xserver.displayManager.xserverArgs =
743 [ "-config ${configFile}"
744 "-xkbdir" "${cfg.xkb.dir}"
745 ] ++ optional (cfg.display != null) ":${toString cfg.display}"
746 ++ optional (cfg.tty != null) "vt${toString cfg.tty}"
747 ++ optional (cfg.dpi != null) "-dpi ${toString cfg.dpi}"
748 ++ optional (cfg.logFile != null) "-logfile ${toString cfg.logFile}"
749 ++ optional (cfg.verbose != null) "-verbose ${toString cfg.verbose}"
750 ++ optional (!cfg.enableTCP) "-nolisten tcp"
751 ++ optional (cfg.autoRepeatDelay != null) "-ardelay ${toString cfg.autoRepeatDelay}"
752 ++ optional (cfg.autoRepeatInterval != null) "-arinterval ${toString cfg.autoRepeatInterval}"
753 ++ optional cfg.terminateOnReset "-terminate";
754
755 services.xserver.modules =
756 concatLists (catAttrs "modules" cfg.drivers) ++
757 [ xorg.xorgserver.out
758 xorg.xf86inputevdev.out
759 ];
760
761 system.checks = singleton (pkgs.runCommand "xkb-validated" {
762 inherit (cfg.xkb) dir model layout variant options;
763 nativeBuildInputs = with pkgs.buildPackages; [ xkbvalidate ];
764 preferLocalBuild = true;
765 } ''
766 ${optionalString (config.environment.sessionVariables ? XKB_CONFIG_ROOT)
767 "export XKB_CONFIG_ROOT=${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
768 }
769 XKB_CONFIG_ROOT="$dir" xkbvalidate "$model" "$layout" "$variant" "$options"
770 touch "$out"
771 '');
772
773 services.xserver.config =
774 ''
775 Section "ServerFlags"
776 Option "AllowMouseOpenFail" "on"
777 Option "DontZap" "${if cfg.enableCtrlAltBackspace then "off" else "on"}"
778 ${indent cfg.serverFlagsSection}
779 EndSection
780
781 Section "Module"
782 ${indent cfg.moduleSection}
783 EndSection
784
785 Section "Monitor"
786 Identifier "Monitor[0]"
787 ${indent cfg.monitorSection}
788 EndSection
789
790 # Additional "InputClass" sections
791 ${flip (concatMapStringsSep "\n") cfg.inputClassSections (inputClassSection: ''
792 Section "InputClass"
793 ${indent inputClassSection}
794 EndSection
795 '')}
796
797
798 Section "ServerLayout"
799 Identifier "Layout[all]"
800 ${indent cfg.serverLayoutSection}
801 # Reference the Screen sections for each driver. This will
802 # cause the X server to try each in turn.
803 ${flip concatMapStrings (filter (d: d.display) cfg.drivers) (d: ''
804 Screen "Screen-${d.name}[0]"
805 '')}
806 EndSection
807
808 # For each supported driver, add a "Device" and "Screen"
809 # section.
810 ${flip concatMapStrings cfg.drivers (driver: ''
811
812 Section "Device"
813 Identifier "Device-${driver.name}[0]"
814 Driver "${driver.driverName or driver.name}"
815 ${indent cfg.deviceSection}
816 ${indent (driver.deviceSection or "")}
817 ${indent xrandrDeviceSection}
818 EndSection
819 ${optionalString driver.display ''
820
821 Section "Screen"
822 Identifier "Screen-${driver.name}[0]"
823 Device "Device-${driver.name}[0]"
824 ${optionalString (cfg.monitorSection != "") ''
825 Monitor "Monitor[0]"
826 ''}
827
828 ${indent cfg.screenSection}
829 ${indent (driver.screenSection or "")}
830
831 ${optionalString (cfg.defaultDepth != 0) ''
832 DefaultDepth ${toString cfg.defaultDepth}
833 ''}
834
835 ${optionalString
836 (
837 driver.name != "virtualbox"
838 &&
839 (cfg.resolutions != [] ||
840 cfg.extraDisplaySettings != "" ||
841 cfg.virtualScreen != null
842 )
843 )
844 (let
845 f = depth:
846 ''
847 SubSection "Display"
848 Depth ${toString depth}
849 ${optionalString (cfg.resolutions != [])
850 "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"}
851 ${indent cfg.extraDisplaySettings}
852 ${optionalString (cfg.virtualScreen != null)
853 "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"}
854 EndSubSection
855 '';
856 in concatMapStrings f [8 16 24]
857 )}
858
859 EndSection
860 ''}
861 '')}
862
863 ${xrandrMonitorSections}
864
865 ${cfg.extraConfig}
866 '';
867
868 fonts.packages = [
869 (if cfg.upscaleDefaultCursor then fontcursormisc_hidpi else pkgs.xorg.fontcursormisc)
870 pkgs.xorg.fontmiscmisc
871 ];
872
873 };
874
875 # uses relatedPackages
876 meta.buildDocsInSandbox = false;
877}