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