1 { config, lib, utils, pkgs, ... }:
8 cfg = config.services.xserver;
12 # Map video driver names to driver packages. FIXME: move into card-specific modules.
14 # Alias so people can keep using "virtualbox" instead of "vboxvideo".
15 virtualbox = { modules = [ xorg.xf86videovboxvideo ]; driverName = "vboxvideo"; };
17 # Alias so that "radeon" uses the xf86-video-ati driver.
18 radeon = { modules = [ xorg.xf86videoati ]; driverName = "ati"; };
20 # modesetting does not have a xf86videomodesetting package as it is included in xorgserver
25 config.fonts.packages ++
26 # We don't want these fonts in fonts.conf, because then modern,
27 # fontconfig-based applications will get horrible bitmapped
28 # Helvetica fonts. It's better to get a substitution (like Nimbus
29 # Sans) than that horror. But we do need the Adobe fonts for some
30 # old non-fontconfig applications. (Possibly this could be done
31 # better using a fontconfig rule.)
32 [ pkgs.xorg.fontadobe100dpi
33 pkgs.xorg.fontadobe75dpi
41 The output name of the monitor, as shown by
42 {manpage}`xrandr(1)` invoked without arguments.
50 Whether this head is treated as the primary monitor,
54 monitorConfig = mkOption {
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)`.
69 # Just enumerate all heads without discarding XRandR output information.
71 mkHead = num: config: {
72 name = "multihead${toString num}";
75 in imap1 mkHead cfg.xrandrHeads;
77 xrandrDeviceSection = let
78 monitors = forEach xrandrHeads (h: ''
79 Option "monitor-${h.config.output}" "${h.name}"
81 in concatStrings monitors;
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;
92 Identifier "${current.name}"
93 ${optionalString (current.config.primary) ''
94 Option "Primary" "true"
96 ${optionalString (previous != []) ''
97 Option "RightOf" "${(head previous).name}"
99 ${current.config.monitorConfig}
103 monitors = reverseList (foldl mkMonitor [] xrandrHeads);
104 in concatMapStrings (getAttr "value") monitors;
106 configFile = pkgs.runCommand "xserver.conf"
107 { fontpath = optionalString (cfg.fontPath != null)
108 ''FontPath "${cfg.fontPath}"'';
109 inherit (cfg) config;
110 preferLocalBuild = true;
113 echo 'Section "Files"' >> $out
114 echo "$fontpath" >> $out
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
124 ${concatMapStrings (m: ''
125 echo " ModulePath \"${m}/lib/xorg/modules\"" >> "$out"
128 echo '${cfg.filesSection}' >> $out
129 echo 'EndSection' >> $out
132 echo "$config" >> $out
135 prefixStringLines = prefix: str:
136 concatMapStringsSep "\n" (line: prefix + line) (splitString "\n" str);
138 indent = prefixStringLines " ";
140 # A scalable variant of the X11 "core" cursor
142 # If not running a fancy desktop environment, the cursor is likely set to
143 # the default `cursor.pcf` bitmap font. This is 17px wide, so it's very
144 # small and almost invisible on 4K displays.
145 fontcursormisc_hidpi = pkgs.xorg.fontxfree86type1.overrideAttrs (old:
147 # The scaling constant is 230/96: the scalable `left_ptr` glyph at
148 # about 23 points is rendered as 17px, on a 96dpi display.
149 # Note: the XLFD font size is in decipoints.
150 size = 2.39583 * cfg.dpi;
151 sizeString = builtins.head (builtins.split "\\." (toString size));
155 alias='cursor -xfree86-cursor-medium-r-normal--0-${sizeString}-0-0-p-0-adobe-fontspecific'
156 echo "$alias" > $out/lib/X11/fonts/Type1/fonts.alias
164 [ ./display-managers/default.nix
165 ./window-managers/default.nix
166 ./desktop-managers/default.nix
167 (mkRemovedOptionModule [ "services" "xserver" "startGnuPGAgent" ]
168 "See the 16.09 release notes for more information.")
169 (mkRemovedOptionModule
170 [ "services" "xserver" "startDbusSession" ]
171 "The user D-Bus session is now always socket activated and this option can safely be removed.")
172 (mkRemovedOptionModule [ "services" "xserver" "useXFS" ]
173 "Use services.xserver.fontPath instead of useXFS")
174 (mkRemovedOptionModule [ "services" "xserver" "useGlamor" ]
175 "Option services.xserver.useGlamor was removed because it is unnecessary. Drivers that uses Glamor will use it automatically.")
176 (lib.mkRenamedOptionModuleWith {
178 from = [ "services" "xserver" "layout" ];
179 to = [ "services" "xserver" "xkb" "layout" ];
181 (lib.mkRenamedOptionModuleWith {
183 from = [ "services" "xserver" "xkbModel" ];
184 to = [ "services" "xserver" "xkb" "model" ];
186 (lib.mkRenamedOptionModuleWith {
188 from = [ "services" "xserver" "xkbOptions" ];
189 to = [ "services" "xserver" "xkb" "options" ];
191 (lib.mkRenamedOptionModuleWith {
193 from = [ "services" "xserver" "xkbVariant" ];
194 to = [ "services" "xserver" "xkb" "variant" ];
196 (lib.mkRenamedOptionModuleWith {
198 from = [ "services" "xserver" "xkbDir" ];
199 to = [ "services" "xserver" "xkb" "dir" ];
214 Whether to enable the X server.
222 Whether to start the X server automatically.
226 excludePackages = mkOption {
228 example = literalExpression "[ pkgs.xterm ]";
229 type = types.listOf types.package;
230 description = "Which X11 packages to exclude from the default environment";
233 exportConfiguration = mkOption {
237 Whether to symlink the X server configuration under
238 {file}`/etc/X11/xorg.conf`.
242 enableTCP = mkOption {
246 Whether to allow the X server to accept TCP connections.
250 autoRepeatDelay = mkOption {
251 type = types.nullOr types.int;
254 Sets the autorepeat delay (length of time in milliseconds that a key must be depressed before autorepeat starts).
258 autoRepeatInterval = mkOption {
259 type = types.nullOr types.int;
262 Sets the autorepeat interval (length of time in milliseconds that should elapse between autorepeat-generated keystrokes).
266 inputClassSections = mkOption {
267 type = types.listOf types.lines;
269 example = literalExpression ''
271 Identifier "Trackpoint Wheel Emulation"
272 MatchProduct "ThinkPad USB Keyboard with TrackPoint"
273 Option "EmulateWheel" "true"
274 Option "EmulateWheelButton" "2"
275 Option "Emulate3Buttons" "false"
279 description = "Content of additional InputClass sections of the X server configuration file.";
283 type = types.listOf types.path;
285 example = literalExpression "[ pkgs.xf86_input_wacom ]";
286 description = "Packages to be added to the module search path of the X server.";
289 resolutions = mkOption {
290 type = types.listOf types.attrs;
292 example = [ { x = 1600; y = 1200; } { x = 1024; y = 786; } ];
294 The screen resolutions for the X server. The first element
295 is the default resolution. If this list is empty, the X
296 server will automatically configure the resolution.
300 videoDrivers = mkOption {
301 type = types.listOf types.str;
302 default = [ "modesetting" "fbdev" ];
307 # TODO(@oxij): think how to easily add the rest, like those nvidia things
308 relatedPackages = concatLists
309 (mapAttrsToList (n: v:
310 optional (hasPrefix "xf86video" n) {
312 title = removePrefix "xf86video" n;
315 The names of the video drivers the configuration
316 supports. They will be tried in order until one that
317 supports your card is found.
318 Don't combine those with "incompatible" OpenGL implementations,
319 e.g. free ones (mesa-based) with proprietary ones.
321 For unfree "nvidia*", the supported GPU lists are on
322 https://www.nvidia.com/object/unix.html
326 videoDriver = mkOption {
327 type = types.nullOr types.str;
331 The name of the video driver for your graphics card. This
332 option is obsolete; please set the
333 {option}`services.xserver.videoDrivers` instead.
338 type = types.listOf types.attrs;
341 A list of attribute sets specifying drivers to be loaded by
347 type = types.nullOr types.int;
350 Force global DPI resolution to use for X server. It's recommended to
351 use this only when DPI is detected incorrectly; also consider using
352 `Monitor` section in configuration file instead.
356 updateDbusEnvironment = mkOption {
360 Whether to update the DBus activation environment after launching the
370 X keyboard layout, or multiple keyboard layouts separated by commas.
377 example = "presario";
385 default = "terminate:ctrl_alt_bksp";
386 example = "grp:caps_toggle,grp_led:scroll";
388 X keyboard options; layout switching goes here.
403 default = "${pkgs.xkeyboard_config}/etc/X11/xkb";
404 defaultText = literalExpression ''"''${pkgs.xkeyboard_config}/etc/X11/xkb"'';
406 Path used for -xkbdir xserver parameter.
414 The contents of the configuration file of the X server
417 This option is set by multiple modules, and the configs are
418 concatenated together.
420 In Xorg configs the last config entries take precedence,
421 so you may want to use `lib.mkAfter` on this option
422 to override NixOS's defaults.
426 filesSection = mkOption {
429 example = ''FontPath "/path/to/my/fonts"'';
430 description = "Contents of the first `Files` section of the X server configuration file.";
433 deviceSection = mkOption {
436 example = "VideoRAM 131072";
437 description = "Contents of the first Device section of the X server configuration file.";
440 screenSection = mkOption {
444 Option "RandRRotation" "on"
446 description = "Contents of the first Screen section of the X server configuration file.";
449 monitorSection = mkOption {
452 example = "HorizSync 28-49";
453 description = "Contents of the first Monitor section of the X server configuration file.";
456 enableTearFree = mkEnableOption "the TearFree option in the first Device section";
458 extraConfig = mkOption {
461 description = "Additional contents (sections) included in the X server configuration file";
464 xrandrHeads = mkOption {
468 { output = "DVI-0"; primary = true; }
469 { output = "DVI-1"; monitorConfig = "Option \"Rotate\" \"left\""; }
471 type = with types; listOf (coercedTo str (output: {
473 }) (submodule { options = xrandrOptions; }));
474 # Set primary to true for the first head if no other has been set
477 hasPrimary = any (x: x.primary) heads;
478 firstPrimary = head heads // { primary = true; };
479 newHeads = singleton firstPrimary ++ tail heads;
480 in if heads != [] && !hasPrimary then newHeads else heads;
482 Multiple monitor configuration, just specify a list of XRandR
483 outputs. The individual elements should be either simple strings or
484 an attribute set of output options.
486 If the element is a string, it is denoting the physical output for a
487 monitor, if it's an attribute set, you must at least provide the
488 {option}`output` option.
490 The monitors will be mapped from left to right in the order of the
493 By default, the first monitor will be set as the primary monitor if
494 none of the elements contain an option that has set
495 {option}`primary` to `true`.
498 Only one monitor is allowed to be primary.
501 Be careful using this option with multiple graphic adapters or with
502 drivers that have poor support for XRandR, unexpected things might
507 serverFlagsSection = mkOption {
512 Option "BlankTime" "0"
513 Option "StandbyTime" "0"
514 Option "SuspendTime" "0"
517 description = "Contents of the ServerFlags section of the X server configuration file.";
520 moduleSection = mkOption {
528 description = "Contents of the Module section of the X server configuration file.";
531 serverLayoutSection = mkOption {
536 Option "AIGLX" "true"
538 description = "Contents of the ServerLayout section of the X server configuration file.";
541 extraDisplaySettings = mkOption {
544 example = "Virtual 2048 2048";
545 description = "Lines to be added to every Display subsection of the Screen section.";
548 defaultDepth = mkOption {
552 description = "Default colour depth.";
555 fontPath = mkOption {
556 type = types.nullOr types.str;
558 example = "unix/:7100";
560 Set the X server FontPath. Defaults to null, which
561 means the compiled in defaults will be used. See
562 man xorg.conf for details.
567 type = types.nullOr types.int;
569 description = "Virtual console for the X server.";
573 type = types.nullOr types.int;
575 description = "Display number for the X server.";
578 virtualScreen = mkOption {
579 type = types.nullOr types.attrs;
581 example = { x = 2048; y = 2048; };
583 Virtual screen size for Xrandr.
588 type = types.nullOr types.str;
589 default = "/dev/null";
590 example = "/var/log/Xorg.0.log";
592 Controls the file Xorg logs to.
594 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.
596 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`).
601 type = types.nullOr types.int;
605 Controls verbosity of X logging.
609 enableCtrlAltBackspace = mkOption {
613 Whether to enable the DontZap option, which binds Ctrl+Alt+Backspace
614 to forcefully kill X. This can lead to data loss and is disabled
619 terminateOnReset = mkOption {
623 Whether to terminate X upon server reset.
627 upscaleDefaultCursor = mkOption {
631 Upscale the default X cursor to be more visible on high-density displays.
632 Requires `config.services.xserver.dpi` to be set.
641 ###### implementation
643 config = mkIf cfg.enable {
644 services.displayManager.enable = true;
646 services.xserver.displayManager.lightdm.enable =
647 let dmConf = cfg.displayManager;
648 default = !(dmConf.gdm.enable
649 || config.services.displayManager.sddm.enable
650 || dmConf.xpra.enable
652 || dmConf.startx.enable
653 || config.services.greetd.enable
654 || config.services.displayManager.ly.enable);
655 in mkIf (default) (mkDefault true);
657 services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];
659 # FIXME: somehow check for unknown driver names.
660 services.xserver.drivers = flip concatMap cfg.videoDrivers (name:
663 (if xorg ? ${"xf86video" + name}
664 then { modules = [xorg.${"xf86video" + name}]; }
667 in optional (driver != null) ({ inherit name; modules = []; driverName = name; display = true; } // driver));
670 (let primaryHeads = filter (x: x.primary) cfg.xrandrHeads; in {
671 assertion = length primaryHeads < 2;
672 message = "Only one head is allowed to be primary in "
673 + "‘services.xserver.xrandrHeads’, but there are "
674 + "${toString (length primaryHeads)} heads set to primary: "
675 + concatMapStringsSep ", " (x: x.output) primaryHeads;
678 assertion = cfg.upscaleDefaultCursor -> cfg.dpi != null;
679 message = "Specify `config.services.xserver.dpi` to upscale the default cursor.";
684 (optionalAttrs cfg.exportConfiguration
686 "X11/xorg.conf".source = "${configFile}";
687 # -xkbdir command line option does not seems to be passed to xkbcomp.
688 "X11/xkb".source = "${cfg.xkb.dir}";
690 # Needed since 1.18; see https://bugs.freedesktop.org/show_bug.cgi?id=89023#c5
691 // (let cfgPath = "X11/xorg.conf.d/10-evdev.conf"; in
693 ${cfgPath}.source = xorg.xf86inputevdev.out + "/share/" + cfgPath;
696 environment.systemPackages = utils.removePackagesByName
697 [ xorg.xorgserver.out
701 xorg.iceauth # required for KDE applications (it's called by dcopserver)
709 xorg.xf86inputevdev.out # get evdev.4 man page
710 ] config.services.xserver.excludePackages
711 ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh;
713 environment.pathsToLink = [ "/share/X11" ];
715 systemd.services.display-manager =
716 { description = "Display Manager";
718 after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
720 restartIfChanged = false;
722 environment = config.services.displayManager.environment;
726 ${config.services.displayManager.preStart}
731 # Stop restarting if the display manager stops (crashes) 2 times
732 # in one minute. Starting X typically takes 3-4s.
733 startLimitIntervalSec = 30;
737 RestartSec = "200ms";
738 SyslogIdentifier = "display-manager";
742 services.xserver.displayManager.xserverArgs =
743 [ "-config ${configFile}"
744 "-xkbdir" "${cfg.xkb.dir}"
745 ] ++ optional (cfg.display != null) ":${toString cfg.display}"
746 ++ optional (cfg.tty != null) "vt${toString cfg.tty}"
747 ++ optional (cfg.dpi != null) "-dpi ${toString cfg.dpi}"
748 ++ optional (cfg.logFile != null) "-logfile ${toString cfg.logFile}"
749 ++ optional (cfg.verbose != null) "-verbose ${toString cfg.verbose}"
750 ++ optional (!cfg.enableTCP) "-nolisten tcp"
751 ++ optional (cfg.autoRepeatDelay != null) "-ardelay ${toString cfg.autoRepeatDelay}"
752 ++ optional (cfg.autoRepeatInterval != null) "-arinterval ${toString cfg.autoRepeatInterval}"
753 ++ optional cfg.terminateOnReset "-terminate";
755 services.xserver.modules =
756 concatLists (catAttrs "modules" cfg.drivers) ++
757 [ xorg.xorgserver.out
758 xorg.xf86inputevdev.out
761 system.checks = singleton (pkgs.runCommand "xkb-validated" {
762 inherit (cfg.xkb) dir model layout variant options;
763 nativeBuildInputs = with pkgs.buildPackages; [ xkbvalidate ];
764 preferLocalBuild = true;
766 ${optionalString (config.environment.sessionVariables ? XKB_CONFIG_ROOT)
767 "export XKB_CONFIG_ROOT=${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
769 XKB_CONFIG_ROOT="$dir" xkbvalidate "$model" "$layout" "$variant" "$options"
773 services.xserver.config =
775 Section "ServerFlags"
776 Option "AllowMouseOpenFail" "on"
777 Option "DontZap" "${if cfg.enableCtrlAltBackspace then "off" else "on"}"
778 ${indent cfg.serverFlagsSection}
782 ${indent cfg.moduleSection}
786 Identifier "Monitor[0]"
787 ${indent cfg.monitorSection}
790 # Additional "InputClass" sections
791 ${flip (concatMapStringsSep "\n") cfg.inputClassSections (inputClassSection: ''
793 ${indent inputClassSection}
798 Section "ServerLayout"
799 Identifier "Layout[all]"
800 ${indent cfg.serverLayoutSection}
801 # Reference the Screen sections for each driver. This will
802 # cause the X server to try each in turn.
803 ${flip concatMapStrings (filter (d: d.display) cfg.drivers) (d: ''
804 Screen "Screen-${d.name}[0]"
808 # For each supported driver, add a "Device" and "Screen"
810 ${flip concatMapStrings cfg.drivers (driver: ''
813 Identifier "Device-${driver.name}[0]"
814 Driver "${driver.driverName or driver.name}"
815 ${indent (optionalString cfg.enableTearFree ''Option "TearFree" "true"'')}
816 ${indent cfg.deviceSection}
817 ${indent (driver.deviceSection or "")}
818 ${indent xrandrDeviceSection}
820 ${optionalString driver.display ''
823 Identifier "Screen-${driver.name}[0]"
824 Device "Device-${driver.name}[0]"
825 ${optionalString (cfg.monitorSection != "") ''
829 ${indent cfg.screenSection}
830 ${indent (driver.screenSection or "")}
832 ${optionalString (cfg.defaultDepth != 0) ''
833 DefaultDepth ${toString cfg.defaultDepth}
838 driver.name != "virtualbox"
840 (cfg.resolutions != [] ||
841 cfg.extraDisplaySettings != "" ||
842 cfg.virtualScreen != null
849 Depth ${toString depth}
850 ${optionalString (cfg.resolutions != [])
851 "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"}
852 ${indent cfg.extraDisplaySettings}
853 ${optionalString (cfg.virtualScreen != null)
854 "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"}
857 in concatMapStrings f [8 16 24]
864 ${xrandrMonitorSections}
870 (if cfg.upscaleDefaultCursor then fontcursormisc_hidpi else pkgs.xorg.fontcursormisc)
871 pkgs.xorg.fontmiscmisc
876 # uses relatedPackages
877 meta.buildDocsInSandbox = false;