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