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