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 ];
281
282 ###### interface
283
284 options = {
285
286 services.xserver = {
287
288 enable = mkOption {
289 type = types.bool;
290 default = false;
291 description = ''
292 Whether to enable the X server.
293 '';
294 };
295
296 autorun = mkOption {
297 type = types.bool;
298 default = true;
299 description = ''
300 Whether to start the X server automatically.
301 '';
302 };
303
304 excludePackages = mkOption {
305 default = [ ];
306 example = literalExpression "[ pkgs.xterm ]";
307 type = types.listOf types.package;
308 description = "Which X11 packages to exclude from the default environment";
309 };
310
311 exportConfiguration = mkOption {
312 type = types.bool;
313 default = false;
314 description = ''
315 Whether to symlink the X server configuration under
316 {file}`/etc/X11/xorg.conf`.
317 '';
318 };
319
320 enableTCP = mkOption {
321 type = types.bool;
322 default = false;
323 description = ''
324 Whether to allow the X server to accept TCP connections.
325 '';
326 };
327
328 autoRepeatDelay = mkOption {
329 type = types.nullOr types.int;
330 default = null;
331 description = ''
332 Sets the autorepeat delay (length of time in milliseconds that a key must be depressed before autorepeat starts).
333 '';
334 };
335
336 autoRepeatInterval = mkOption {
337 type = types.nullOr types.int;
338 default = null;
339 description = ''
340 Sets the autorepeat interval (length of time in milliseconds that should elapse between autorepeat-generated keystrokes).
341 '';
342 };
343
344 inputClassSections = mkOption {
345 type = types.listOf types.lines;
346 default = [ ];
347 example = literalExpression ''
348 [ '''
349 Identifier "Trackpoint Wheel Emulation"
350 MatchProduct "ThinkPad USB Keyboard with TrackPoint"
351 Option "EmulateWheel" "true"
352 Option "EmulateWheelButton" "2"
353 Option "Emulate3Buttons" "false"
354 '''
355 ]
356 '';
357 description = "Content of additional InputClass sections of the X server configuration file.";
358 };
359
360 modules = mkOption {
361 type = types.listOf types.path;
362 default = [ ];
363 example = literalExpression "[ pkgs.xf86_input_wacom ]";
364 description = "Packages to be added to the module search path of the X server.";
365 };
366
367 resolutions = mkOption {
368 type = types.listOf types.attrs;
369 default = [ ];
370 example = [
371 {
372 x = 1600;
373 y = 1200;
374 }
375 {
376 x = 1024;
377 y = 786;
378 }
379 ];
380 description = ''
381 The screen resolutions for the X server. The first element
382 is the default resolution. If this list is empty, the X
383 server will automatically configure the resolution.
384 '';
385 };
386
387 videoDrivers = mkOption {
388 type = types.listOf types.str;
389 default = [
390 "modesetting"
391 "fbdev"
392 ];
393 example = [
394 "nvidia"
395 "amdgpu"
396 ];
397 # TODO(@oxij): think how to easily add the rest, like those nvidia things
398 relatedPackages = concatLists (
399 mapAttrsToList (
400 n: v:
401 optional (hasPrefix "xf86video" n) {
402 path = [
403 "xorg"
404 n
405 ];
406 title = removePrefix "xf86video" n;
407 }
408 ) pkgs.xorg
409 );
410 description = ''
411 The names of the video drivers the configuration
412 supports. They will be tried in order until one that
413 supports your card is found.
414 Don't combine those with "incompatible" OpenGL implementations,
415 e.g. free ones (mesa-based) with proprietary ones.
416
417 For unfree "nvidia*", the supported GPU lists are on
418 https://www.nvidia.com/object/unix.html
419 '';
420 };
421
422 videoDriver = mkOption {
423 type = types.nullOr types.str;
424 default = null;
425 example = "i810";
426 description = ''
427 The name of the video driver for your graphics card. This
428 option is obsolete; please set the
429 {option}`services.xserver.videoDrivers` instead.
430 '';
431 };
432
433 drivers = mkOption {
434 type = types.listOf types.attrs;
435 internal = true;
436 description = ''
437 A list of attribute sets specifying drivers to be loaded by
438 the X11 server.
439 '';
440 };
441
442 dpi = mkOption {
443 type = types.nullOr types.int;
444 default = null;
445 description = ''
446 Force global DPI resolution to use for X server. It's recommended to
447 use this only when DPI is detected incorrectly; also consider using
448 `Monitor` section in configuration file instead.
449 '';
450 };
451
452 updateDbusEnvironment = mkOption {
453 type = types.bool;
454 default = false;
455 description = ''
456 Whether to update the DBus activation environment after launching the
457 desktop manager.
458 '';
459 };
460
461 xkb = {
462 layout = mkOption {
463 type = types.str;
464 default = "us";
465 description = ''
466 X keyboard layout, or multiple keyboard layouts separated by commas.
467 '';
468 };
469
470 model = mkOption {
471 type = types.str;
472 default = "pc104";
473 example = "presario";
474 description = ''
475 X keyboard model.
476 '';
477 };
478
479 options = mkOption {
480 type = types.commas;
481 default = "terminate:ctrl_alt_bksp";
482 example = "grp:caps_toggle,grp_led:scroll";
483 description = ''
484 X keyboard options; layout switching goes here.
485 '';
486 };
487
488 variant = mkOption {
489 type = types.str;
490 default = "";
491 example = "colemak";
492 description = ''
493 X keyboard variant.
494 '';
495 };
496
497 dir = mkOption {
498 type = types.path;
499 default = "${pkgs.xkeyboard_config}/etc/X11/xkb";
500 defaultText = literalExpression ''"''${pkgs.xkeyboard_config}/etc/X11/xkb"'';
501 description = ''
502 Path used for -xkbdir xserver parameter.
503 '';
504 };
505 };
506
507 config = mkOption {
508 type = types.lines;
509 description = ''
510 The contents of the configuration file of the X server
511 ({file}`xorg.conf`).
512
513 This option is set by multiple modules, and the configs are
514 concatenated together.
515
516 In Xorg configs the last config entries take precedence,
517 so you may want to use `lib.mkAfter` on this option
518 to override NixOS's defaults.
519 '';
520 };
521
522 filesSection = mkOption {
523 type = types.lines;
524 default = "";
525 example = ''FontPath "/path/to/my/fonts"'';
526 description = "Contents of the first `Files` section of the X server configuration file.";
527 };
528
529 deviceSection = mkOption {
530 type = types.lines;
531 default = "";
532 example = "VideoRAM 131072";
533 description = "Contents of the first Device section of the X server configuration file.";
534 };
535
536 screenSection = mkOption {
537 type = types.lines;
538 default = "";
539 example = ''
540 Option "RandRRotation" "on"
541 '';
542 description = "Contents of the first Screen section of the X server configuration file.";
543 };
544
545 monitorSection = mkOption {
546 type = types.lines;
547 default = "";
548 example = "HorizSync 28-49";
549 description = "Contents of the first Monitor section of the X server configuration file.";
550 };
551
552 enableTearFree = mkEnableOption "the TearFree option in the first Device section";
553
554 extraConfig = mkOption {
555 type = types.lines;
556 default = "";
557 description = "Additional contents (sections) included in the X server configuration file";
558 };
559
560 xrandrHeads = mkOption {
561 default = [ ];
562 example = [
563 "HDMI-0"
564 {
565 output = "DVI-0";
566 primary = true;
567 }
568 {
569 output = "DVI-1";
570 monitorConfig = "Option \"Rotate\" \"left\"";
571 }
572 ];
573 type =
574 with types;
575 listOf (
576 coercedTo str
577 (output: {
578 inherit output;
579 })
580 (submodule {
581 options = xrandrOptions;
582 })
583 );
584 # Set primary to true for the first head if no other has been set
585 # primary already.
586 apply =
587 heads:
588 let
589 hasPrimary = any (x: x.primary) heads;
590 firstPrimary = head heads // {
591 primary = true;
592 };
593 newHeads = singleton firstPrimary ++ tail heads;
594 in
595 if heads != [ ] && !hasPrimary then newHeads else heads;
596 description = ''
597 Multiple monitor configuration, just specify a list of XRandR
598 outputs. The individual elements should be either simple strings or
599 an attribute set of output options.
600
601 If the element is a string, it is denoting the physical output for a
602 monitor, if it's an attribute set, you must at least provide the
603 {option}`output` option.
604
605 The monitors will be mapped from left to right in the order of the
606 list.
607
608 By default, the first monitor will be set as the primary monitor if
609 none of the elements contain an option that has set
610 {option}`primary` to `true`.
611
612 ::: {.note}
613 Only one monitor is allowed to be primary.
614 :::
615
616 Be careful using this option with multiple graphic adapters or with
617 drivers that have poor support for XRandR, unexpected things might
618 happen with those.
619 '';
620 };
621
622 serverFlagsSection = mkOption {
623 default = "";
624 type = types.lines;
625 example = ''
626 Option "BlankTime" "0"
627 Option "StandbyTime" "0"
628 Option "SuspendTime" "0"
629 Option "OffTime" "0"
630 '';
631 description = "Contents of the ServerFlags section of the X server configuration file.";
632 };
633
634 moduleSection = mkOption {
635 type = types.lines;
636 default = "";
637 example = ''
638 SubSection "extmod"
639 EndSubsection
640 '';
641 description = "Contents of the Module section of the X server configuration file.";
642 };
643
644 serverLayoutSection = mkOption {
645 type = types.lines;
646 default = "";
647 example = ''
648 Option "AIGLX" "true"
649 '';
650 description = "Contents of the ServerLayout section of the X server configuration file.";
651 };
652
653 extraDisplaySettings = mkOption {
654 type = types.lines;
655 default = "";
656 example = "Virtual 2048 2048";
657 description = "Lines to be added to every Display subsection of the Screen section.";
658 };
659
660 defaultDepth = mkOption {
661 type = types.int;
662 default = 0;
663 example = 8;
664 description = "Default colour depth.";
665 };
666
667 fontPath = mkOption {
668 type = types.nullOr types.str;
669 default = null;
670 example = "unix/:7100";
671 description = ''
672 Set the X server FontPath. Defaults to null, which
673 means the compiled in defaults will be used. See
674 man xorg.conf for details.
675 '';
676 };
677
678 tty = mkOption {
679 type = types.nullOr types.int;
680 default = 7;
681 description = "Virtual console for the X server.";
682 };
683
684 display = mkOption {
685 type = types.nullOr types.int;
686 default = 0;
687 description = "Display number for the X server.";
688 };
689
690 virtualScreen = mkOption {
691 type = types.nullOr types.attrs;
692 default = null;
693 example = {
694 x = 2048;
695 y = 2048;
696 };
697 description = ''
698 Virtual screen size for Xrandr.
699 '';
700 };
701
702 logFile = mkOption {
703 type = types.nullOr types.str;
704 default = "/dev/null";
705 example = "/var/log/Xorg.0.log";
706 description = ''
707 Controls the file Xorg logs to.
708
709 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.
710
711 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`).
712 '';
713 };
714
715 verbose = mkOption {
716 type = types.nullOr types.int;
717 default = 3;
718 example = 7;
719 description = ''
720 Controls verbosity of X logging.
721 '';
722 };
723
724 enableCtrlAltBackspace = mkOption {
725 type = types.bool;
726 default = false;
727 description = ''
728 Whether to enable the DontZap option, which binds Ctrl+Alt+Backspace
729 to forcefully kill X. This can lead to data loss and is disabled
730 by default.
731 '';
732 };
733
734 terminateOnReset = mkOption {
735 type = types.bool;
736 default = true;
737 description = ''
738 Whether to terminate X upon server reset.
739 '';
740 };
741
742 upscaleDefaultCursor = mkOption {
743 type = types.bool;
744 default = false;
745 description = ''
746 Upscale the default X cursor to be more visible on high-density displays.
747 Requires `config.services.xserver.dpi` to be set.
748 '';
749 };
750 };
751
752 };
753
754 ###### implementation
755
756 config = mkIf cfg.enable {
757 services.displayManager.enable = true;
758
759 services.xserver.displayManager.lightdm.enable =
760 let
761 dmConf = cfg.displayManager;
762 default =
763 !(
764 dmConf.gdm.enable
765 || config.services.displayManager.sddm.enable
766 || dmConf.xpra.enable
767 || dmConf.sx.enable
768 || dmConf.startx.enable
769 || config.services.greetd.enable
770 || config.services.displayManager.ly.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 [
884 "-config ${configFile}"
885 "-xkbdir"
886 "${cfg.xkb.dir}"
887 ]
888 ++ optional (cfg.display != null) ":${toString cfg.display}"
889 ++ optional (cfg.tty != null) "vt${toString cfg.tty}"
890 ++ optional (cfg.dpi != null) "-dpi ${toString cfg.dpi}"
891 ++ optional (cfg.logFile != null) "-logfile ${toString cfg.logFile}"
892 ++ optional (cfg.verbose != null) "-verbose ${toString cfg.verbose}"
893 ++ optional (!cfg.enableTCP) "-nolisten tcp"
894 ++ optional (cfg.autoRepeatDelay != null) "-ardelay ${toString cfg.autoRepeatDelay}"
895 ++ optional (cfg.autoRepeatInterval != null) "-arinterval ${toString cfg.autoRepeatInterval}"
896 ++ optional cfg.terminateOnReset "-terminate";
897
898 services.xserver.modules = concatLists (catAttrs "modules" cfg.drivers) ++ [
899 xorg.xorgserver.out
900 xorg.xf86inputevdev.out
901 ];
902
903 system.checks = singleton (
904 pkgs.runCommand "xkb-validated"
905 {
906 inherit (cfg.xkb)
907 dir
908 model
909 layout
910 variant
911 options
912 ;
913 nativeBuildInputs = with pkgs.buildPackages; [ xkbvalidate ];
914 preferLocalBuild = true;
915 }
916 ''
917 ${optionalString (
918 config.environment.sessionVariables ? XKB_CONFIG_ROOT
919 ) "export XKB_CONFIG_ROOT=${config.environment.sessionVariables.XKB_CONFIG_ROOT}"}
920 XKB_CONFIG_ROOT="$dir" xkbvalidate "$model" "$layout" "$variant" "$options"
921 touch "$out"
922 ''
923 );
924
925 services.xserver.config = ''
926 Section "ServerFlags"
927 Option "AllowMouseOpenFail" "on"
928 Option "DontZap" "${if cfg.enableCtrlAltBackspace then "off" else "on"}"
929 ${indent cfg.serverFlagsSection}
930 EndSection
931
932 Section "Module"
933 ${indent cfg.moduleSection}
934 EndSection
935
936 Section "Monitor"
937 Identifier "Monitor[0]"
938 ${indent cfg.monitorSection}
939 EndSection
940
941 # Additional "InputClass" sections
942 ${flip (concatMapStringsSep "\n") cfg.inputClassSections (inputClassSection: ''
943 Section "InputClass"
944 ${indent inputClassSection}
945 EndSection
946 '')}
947
948
949 Section "ServerLayout"
950 Identifier "Layout[all]"
951 ${indent cfg.serverLayoutSection}
952 # Reference the Screen sections for each driver. This will
953 # cause the X server to try each in turn.
954 ${flip concatMapStrings (filter (d: d.display) cfg.drivers) (d: ''
955 Screen "Screen-${d.name}[0]"
956 '')}
957 EndSection
958
959 # For each supported driver, add a "Device" and "Screen"
960 # section.
961 ${flip concatMapStrings cfg.drivers (driver: ''
962
963 Section "Device"
964 Identifier "Device-${driver.name}[0]"
965 Driver "${driver.driverName or driver.name}"
966 ${indent (optionalString cfg.enableTearFree ''Option "TearFree" "true"'')}
967 ${indent cfg.deviceSection}
968 ${indent (driver.deviceSection or "")}
969 ${indent xrandrDeviceSection}
970 EndSection
971 ${optionalString driver.display ''
972
973 Section "Screen"
974 Identifier "Screen-${driver.name}[0]"
975 Device "Device-${driver.name}[0]"
976 ${optionalString (cfg.monitorSection != "") ''
977 Monitor "Monitor[0]"
978 ''}
979
980 ${indent cfg.screenSection}
981 ${indent (driver.screenSection or "")}
982
983 ${optionalString (cfg.defaultDepth != 0) ''
984 DefaultDepth ${toString cfg.defaultDepth}
985 ''}
986
987 ${optionalString
988 (
989 driver.name != "virtualbox"
990 && (cfg.resolutions != [ ] || cfg.extraDisplaySettings != "" || cfg.virtualScreen != null)
991 )
992 (
993 let
994 f = depth: ''
995 SubSection "Display"
996 Depth ${toString depth}
997 ${optionalString (cfg.resolutions != [ ])
998 "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"
999 }
1000 ${indent cfg.extraDisplaySettings}
1001 ${optionalString (
1002 cfg.virtualScreen != null
1003 ) "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"}
1004 EndSubSection
1005 '';
1006 in
1007 concatMapStrings f [
1008 8
1009 16
1010 24
1011 ]
1012 )
1013 }
1014
1015 EndSection
1016 ''}
1017 '')}
1018
1019 ${xrandrMonitorSections}
1020
1021 ${cfg.extraConfig}
1022 '';
1023
1024 fonts.packages = [
1025 (if cfg.upscaleDefaultCursor then fontcursormisc_hidpi else pkgs.xorg.fontcursormisc)
1026 pkgs.xorg.fontmiscmisc
1027 ];
1028
1029 };
1030
1031 # uses relatedPackages
1032 meta.buildDocsInSandbox = false;
1033}