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 ati = { modules = [ pkgs.xorg.xf86videoati pkgs.xorg.glamoregl ]; }; 18 intel-testing = { modules = with pkgs.xorg; [ xf86videointel-testing glamoregl ]; driverName = "intel"; }; 19 }; 20 21 fontsForXServer = 22 config.fonts.fonts ++ 23 # We don't want these fonts in fonts.conf, because then modern, 24 # fontconfig-based applications will get horrible bitmapped 25 # Helvetica fonts. It's better to get a substitution (like Nimbus 26 # Sans) than that horror. But we do need the Adobe fonts for some 27 # old non-fontconfig applications. (Possibly this could be done 28 # better using a fontconfig rule.) 29 [ pkgs.xorg.fontadobe100dpi 30 pkgs.xorg.fontadobe75dpi 31 ]; 32 33 34 # Just enumerate all heads without discarding XRandR output information. 35 xrandrHeads = let 36 mkHead = num: output: { 37 name = "multihead${toString num}"; 38 inherit output; 39 }; 40 in imap mkHead cfg.xrandrHeads; 41 42 xrandrDeviceSection = let 43 monitors = flip map xrandrHeads (h: '' 44 Option "monitor-${h.output}" "${h.name}" 45 ''); 46 # First option is indented through the space in the config but any 47 # subsequent options aren't so we need to apply indentation to 48 # them here 49 monitorsIndented = if length monitors > 1 50 then singleton (head monitors) ++ map (m: " " + m) (tail monitors) 51 else monitors; 52 in concatStrings monitorsIndented; 53 54 # Here we chain every monitor from the left to right, so we have: 55 # m4 right of m3 right of m2 right of m1 .----.----.----.----. 56 # Which will end up in reverse ----------> | m1 | m2 | m3 | m4 | 57 # `----^----^----^----' 58 xrandrMonitorSections = let 59 mkMonitor = previous: current: singleton { 60 inherit (current) name; 61 value = '' 62 Section "Monitor" 63 Identifier "${current.name}" 64 ${optionalString (previous != []) '' 65 Option "RightOf" "${(head previous).name}" 66 ''} 67 EndSection 68 ''; 69 } ++ previous; 70 monitors = reverseList (foldl mkMonitor [] xrandrHeads); 71 in concatMapStrings (getAttr "value") monitors; 72 73 configFile = pkgs.stdenv.mkDerivation { 74 name = "xserver.conf"; 75 76 xfs = optionalString (cfg.useXFS != false) 77 ''FontPath "${toString cfg.useXFS}"''; 78 79 inherit (cfg) config; 80 81 buildCommand = 82 '' 83 echo 'Section "Files"' >> $out 84 echo $xfs >> $out 85 86 for i in ${toString fontsForXServer}; do 87 if test "''${i:0:''${#NIX_STORE}}" == "$NIX_STORE"; then 88 for j in $(find $i -name fonts.dir); do 89 echo " FontPath \"$(dirname $j)\"" >> $out 90 done 91 fi 92 done 93 94 for i in $(find ${toString cfg.modules} -type d); do 95 if test $(echo $i/*.so* | wc -w) -ne 0; then 96 echo " ModulePath \"$i\"" >> $out 97 fi 98 done 99 100 echo 'EndSection' >> $out 101 102 echo "$config" >> $out 103 ''; # */ 104 }; 105 106in 107 108{ 109 110 imports = 111 [ ./display-managers/default.nix 112 ./window-managers/default.nix 113 ./desktop-managers/default.nix 114 ]; 115 116 117 ###### interface 118 119 options = { 120 121 services.xserver = { 122 123 enable = mkOption { 124 type = types.bool; 125 default = false; 126 description = '' 127 Whether to enable the X server. 128 ''; 129 }; 130 131 autorun = mkOption { 132 type = types.bool; 133 default = true; 134 description = '' 135 Whether to start the X server automatically. 136 ''; 137 }; 138 139 exportConfiguration = mkOption { 140 type = types.bool; 141 default = false; 142 description = '' 143 Whether to symlink the X server configuration under 144 <filename>/etc/X11/xorg.conf</filename>. 145 ''; 146 }; 147 148 enableTCP = mkOption { 149 type = types.bool; 150 default = false; 151 description = '' 152 Whether to allow the X server to accept TCP connections. 153 ''; 154 }; 155 156 inputClassSections = mkOption { 157 type = types.listOf types.lines; 158 default = []; 159 example = literalExample '' 160 [ ''' 161 Identifier "Trackpoint Wheel Emulation" 162 MatchProduct "ThinkPad USB Keyboard with TrackPoint" 163 Option "EmulateWheel" "true 164 Option "EmulateWheelButton" "2" 165 Option "Emulate3Buttons" "false" 166 ''' 167 ] 168 ''; 169 description = "Content of additional InputClass sections of the X server configuration file."; 170 }; 171 172 modules = mkOption { 173 type = types.listOf types.path; 174 default = []; 175 example = literalExample "[ pkgs.xf86_input_wacom ]"; 176 description = "Packages to be added to the module search path of the X server."; 177 }; 178 179 resolutions = mkOption { 180 type = types.listOf types.attrs; 181 default = []; 182 example = [ { x = 1600; y = 1200; } { x = 1024; y = 786; } ]; 183 description = '' 184 The screen resolutions for the X server. The first element 185 is the default resolution. If this list is empty, the X 186 server will automatically configure the resolution. 187 ''; 188 }; 189 190 videoDrivers = mkOption { 191 type = types.listOf types.str; 192 # !!! We'd like "nv" here, but it segfaults the X server. 193 default = [ "ati" "cirrus" "intel" "vesa" "vmware" "modesetting" ]; 194 example = [ "vesa" ]; 195 description = '' 196 The names of the video drivers the configuration 197 supports. They will be tried in order until one that 198 supports your card is found. 199 ''; 200 }; 201 202 videoDriver = mkOption { 203 type = types.nullOr types.str; 204 default = null; 205 example = "i810"; 206 description = '' 207 The name of the video driver for your graphics card. This 208 option is obsolete; please set the 209 <option>services.xserver.videoDrivers</option> instead. 210 ''; 211 }; 212 213 drivers = mkOption { 214 type = types.listOf types.attrs; 215 internal = true; 216 description = '' 217 A list of attribute sets specifying drivers to be loaded by 218 the X11 server. 219 ''; 220 }; 221 222 startGnuPGAgent = mkOption { 223 type = types.bool; 224 default = false; 225 description = '' 226 Whether to start the GnuPG agent when you log in. The GnuPG agent 227 remembers private keys for you so that you don't have to type in 228 passphrases every time you make an SSH connection or sign/encrypt 229 data. Use <command>ssh-add</command> to add a key to the agent. 230 ''; 231 }; 232 233 startDbusSession = mkOption { 234 type = types.bool; 235 default = true; 236 description = '' 237 Whether to start a new DBus session when you log in with dbus-launch. 238 ''; 239 }; 240 241 layout = mkOption { 242 type = types.str; 243 default = "us"; 244 description = '' 245 Keyboard layout. 246 ''; 247 }; 248 249 xkbModel = mkOption { 250 type = types.str; 251 default = "pc104"; 252 example = "presario"; 253 description = '' 254 Keyboard model. 255 ''; 256 }; 257 258 xkbOptions = mkOption { 259 type = types.str; 260 default = "terminate:ctrl_alt_bksp"; 261 example = "grp:caps_toggle, grp_led:scroll"; 262 description = '' 263 X keyboard options; layout switching goes here. 264 ''; 265 }; 266 267 xkbVariant = mkOption { 268 type = types.str; 269 default = ""; 270 example = "colemak"; 271 description = '' 272 X keyboard variant. 273 ''; 274 }; 275 276 xkbDir = mkOption { 277 type = types.path; 278 description = '' 279 Path used for -xkbdir xserver parameter. 280 ''; 281 }; 282 283 config = mkOption { 284 type = types.lines; 285 description = '' 286 The contents of the configuration file of the X server 287 (<filename>xorg.conf</filename>). 288 ''; 289 }; 290 291 deviceSection = mkOption { 292 type = types.lines; 293 default = ""; 294 example = "VideoRAM 131072"; 295 description = "Contents of the first Device section of the X server configuration file."; 296 }; 297 298 screenSection = mkOption { 299 type = types.lines; 300 default = ""; 301 example = '' 302 Option "RandRRotation" "on" 303 ''; 304 description = "Contents of the first Screen section of the X server configuration file."; 305 }; 306 307 monitorSection = mkOption { 308 type = types.lines; 309 default = ""; 310 example = "HorizSync 28-49"; 311 description = "Contents of the first Monitor section of the X server configuration file."; 312 }; 313 314 xrandrHeads = mkOption { 315 default = []; 316 example = [ "HDMI-0" "DVI-0" ]; 317 type = with types; listOf string; 318 description = '' 319 Simple multiple monitor configuration, just specify a list of XRandR 320 outputs which will be mapped from left to right in the order of the 321 list. 322 323 Be careful using this option with multiple graphic adapters or with 324 drivers that have poor support for XRandR, unexpected things might 325 happen with those. 326 ''; 327 }; 328 329 serverFlagsSection = mkOption { 330 default = ""; 331 example = 332 '' 333 Option "BlankTime" "0" 334 Option "StandbyTime" "0" 335 Option "SuspendTime" "0" 336 Option "OffTime" "0" 337 ''; 338 description = "Contents of the ServerFlags section of the X server configuration file."; 339 }; 340 341 moduleSection = mkOption { 342 type = types.lines; 343 default = ""; 344 example = 345 '' 346 SubSection "extmod" 347 EndSubsection 348 ''; 349 description = "Contents of the Module section of the X server configuration file."; 350 }; 351 352 serverLayoutSection = mkOption { 353 type = types.lines; 354 default = ""; 355 example = 356 '' 357 Option "AIGLX" "true" 358 ''; 359 description = "Contents of the ServerLayout section of the X server configuration file."; 360 }; 361 362 extraDisplaySettings = mkOption { 363 type = types.lines; 364 default = ""; 365 example = "Virtual 2048 2048"; 366 description = "Lines to be added to every Display subsection of the Screen section."; 367 }; 368 369 defaultDepth = mkOption { 370 type = types.int; 371 default = 0; 372 example = 8; 373 description = "Default colour depth."; 374 }; 375 376 useXFS = mkOption { 377 # FIXME: what's the type of this option? 378 default = false; 379 example = "unix/:7100"; 380 description = "Determines how to connect to the X Font Server."; 381 }; 382 383 tty = mkOption { 384 type = types.nullOr types.int; 385 default = 7; 386 description = "Virtual console for the X server."; 387 }; 388 389 display = mkOption { 390 type = types.nullOr types.int; 391 default = 0; 392 description = "Display number for the X server."; 393 }; 394 395 virtualScreen = mkOption { 396 type = types.nullOr types.attrs; 397 default = null; 398 example = { x = 2048; y = 2048; }; 399 description = '' 400 Virtual screen size for Xrandr. 401 ''; 402 }; 403 404 useGlamor = mkOption { 405 type = types.bool; 406 default = false; 407 description = '' 408 Whether to use the Glamor module for 2D acceleration, 409 if possible. 410 ''; 411 }; 412 413 enableCtrlAltBackspace = mkOption { 414 type = types.bool; 415 default = false; 416 description = '' 417 Whether to enable the DontZap option, which binds Ctrl+Alt+Backspace 418 to forcefully kill X. This can lead to data loss and is disabled 419 by default. 420 ''; 421 }; 422 }; 423 424 }; 425 426 427 428 ###### implementation 429 430 config = mkIf cfg.enable { 431 432 hardware.opengl.enable = mkDefault true; 433 434 services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ]; 435 436 # FIXME: somehow check for unknown driver names. 437 services.xserver.drivers = flip concatMap cfg.videoDrivers (name: 438 let driver = 439 attrByPath [name] 440 (if xorg ? ${"xf86video" + name} 441 then { modules = [xorg.${"xf86video" + name}]; } 442 else null) 443 knownVideoDrivers; 444 in optional (driver != null) ({ inherit name; driverName = name; } // driver)); 445 446 assertions = 447 [ { assertion = !(config.programs.ssh.startAgent && cfg.startGnuPGAgent); 448 message = 449 '' 450 The OpenSSH agent and GnuPG agent cannot be started both. Please 451 choose between programs.ssh.startAgent and services.xserver.startGnuPGAgent. 452 ''; 453 } 454 { assertion = config.security.polkit.enable; 455 message = "X11 requires Polkit to be enabled (security.polkit.enable = true)."; 456 } 457 ]; 458 459 environment.etc = 460 (optionals cfg.exportConfiguration 461 [ { source = "${configFile}"; 462 target = "X11/xorg.conf"; 463 } 464 # -xkbdir command line option does not seems to be passed to xkbcomp. 465 { source = "${cfg.xkbDir}"; 466 target = "X11/xkb"; 467 } 468 ]); 469 470 environment.systemPackages = 471 [ xorg.xorgserver 472 xorg.xrandr 473 xorg.xrdb 474 xorg.setxkbmap 475 xorg.iceauth # required for KDE applications (it's called by dcopserver) 476 xorg.xlsclients 477 xorg.xset 478 xorg.xsetroot 479 xorg.xinput 480 xorg.xprop 481 pkgs.xterm 482 pkgs.xdg_utils 483 ] 484 ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh; 485 486 environment.pathsToLink = 487 [ "/etc/xdg" "/share/xdg" "/share/applications" "/share/icons" "/share/pixmaps" ]; 488 489 # The default max inotify watches is 8192. 490 # Nowadays most apps require a good number of inotify watches, 491 # the value below is used by default on several other distros. 492 boot.kernel.sysctl."fs.inotify.max_user_watches" = mkDefault 524288; 493 494 systemd.defaultUnit = mkIf cfg.autorun "graphical.target"; 495 496 systemd.services.display-manager = 497 { description = "X11 Server"; 498 499 after = [ "systemd-udev-settle.service" "local-fs.target" "acpid.service" "systemd-logind.service" ]; 500 501 restartIfChanged = false; 502 503 environment = 504 { 505 XKB_BINDIR = "${xorg.xkbcomp}/bin"; # Needed for the Xkb extension. 506 XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime. 507 LD_LIBRARY_PATH = concatStringsSep ":" ( 508 [ "${xorg.libX11}/lib" "${xorg.libXext}/lib" ] 509 ++ concatLists (catAttrs "libPath" cfg.drivers)); 510 } // cfg.displayManager.job.environment; 511 512 preStart = 513 '' 514 ${cfg.displayManager.job.preStart} 515 516 rm -f /tmp/.X0-lock 517 ''; 518 519 script = "${cfg.displayManager.job.execCmd}"; 520 521 serviceConfig = { 522 Restart = "always"; 523 RestartSec = "200ms"; 524 }; 525 }; 526 527 services.xserver.displayManager.xserverArgs = 528 [ "-ac" 529 "-terminate" 530 "-config ${configFile}" 531 "-xkbdir" "${cfg.xkbDir}" 532 ] ++ optional (cfg.display != null) ":${toString cfg.display}" 533 ++ optional (cfg.tty != null) "vt${toString cfg.tty}" 534 ++ optionals (cfg.display != null) [ "-logfile" "/var/log/X.${toString cfg.display}.log" ] 535 ++ optional (!cfg.enableTCP) "-nolisten tcp"; 536 537 services.xserver.modules = 538 concatLists (catAttrs "modules" cfg.drivers) ++ 539 [ xorg.xorgserver 540 xorg.xf86inputevdev 541 ]; 542 543 services.xserver.xkbDir = mkDefault "${pkgs.xkeyboard_config}/etc/X11/xkb"; 544 545 services.xserver.config = 546 '' 547 Section "ServerFlags" 548 Option "AllowMouseOpenFail" "on" 549 Option "DontZap" "${if cfg.enableCtrlAltBackspace then "off" else "on"}" 550 ${cfg.serverFlagsSection} 551 EndSection 552 553 Section "Module" 554 ${cfg.moduleSection} 555 EndSection 556 557 Section "Monitor" 558 Identifier "Monitor[0]" 559 ${cfg.monitorSection} 560 EndSection 561 562 Section "InputClass" 563 Identifier "Keyboard catchall" 564 MatchIsKeyboard "on" 565 Option "XkbRules" "base" 566 Option "XkbModel" "${cfg.xkbModel}" 567 Option "XkbLayout" "${cfg.layout}" 568 Option "XkbOptions" "${cfg.xkbOptions}" 569 Option "XkbVariant" "${cfg.xkbVariant}" 570 EndSection 571 572 # Additional "InputClass" sections 573 ${flip concatMapStrings cfg.inputClassSections (inputClassSection: '' 574 Section "InputClass" 575 ${inputClassSection} 576 EndSection 577 '')} 578 579 580 Section "ServerLayout" 581 Identifier "Layout[all]" 582 ${cfg.serverLayoutSection} 583 # Reference the Screen sections for each driver. This will 584 # cause the X server to try each in turn. 585 ${flip concatMapStrings cfg.drivers (d: '' 586 Screen "Screen-${d.name}[0]" 587 '')} 588 EndSection 589 590 ${if cfg.useGlamor then '' 591 Section "Module" 592 Load "dri2" 593 Load "glamoregl" 594 EndSection 595 '' else ""} 596 597 # For each supported driver, add a "Device" and "Screen" 598 # section. 599 ${flip concatMapStrings cfg.drivers (driver: '' 600 601 Section "Device" 602 Identifier "Device-${driver.name}[0]" 603 Driver "${driver.driverName or driver.name}" 604 ${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""} 605 ${cfg.deviceSection} 606 ${xrandrDeviceSection} 607 EndSection 608 609 Section "Screen" 610 Identifier "Screen-${driver.name}[0]" 611 Device "Device-${driver.name}[0]" 612 ${optionalString (cfg.monitorSection != "") '' 613 Monitor "Monitor[0]" 614 ''} 615 616 ${cfg.screenSection} 617 618 ${optionalString (cfg.defaultDepth != 0) '' 619 DefaultDepth ${toString cfg.defaultDepth} 620 ''} 621 622 ${optionalString 623 (driver.name != "virtualbox" && 624 (cfg.resolutions != [] || 625 cfg.extraDisplaySettings != "" || 626 cfg.virtualScreen != null)) 627 (let 628 f = depth: 629 '' 630 SubSection "Display" 631 Depth ${toString depth} 632 ${optionalString (cfg.resolutions != []) 633 "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"} 634 ${cfg.extraDisplaySettings} 635 ${optionalString (cfg.virtualScreen != null) 636 "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"} 637 EndSubSection 638 ''; 639 in concatMapStrings f [8 16 24] 640 )} 641 642 EndSection 643 '')} 644 645 ${xrandrMonitorSections} 646 ''; 647 648 }; 649 650}