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