at 18.09-beta 25 kB view raw
1{ config, lib, pkgs, ... }: 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 = [ 244 "ati_unfree" "amdgpu" "amdgpu-pro" 245 "nv" "nvidia" "nvidiaLegacy340" "nvidiaLegacy304" 246 ]; 247 # TODO(@oxij): think how to easily add the rest, like those nvidia things 248 relatedPackages = concatLists 249 (mapAttrsToList (n: v: 250 optional (hasPrefix "xf86video" n) { 251 path = [ "xorg" n ]; 252 title = removePrefix "xf86video" n; 253 }) pkgs.xorg); 254 description = '' 255 The names of the video drivers the configuration 256 supports. They will be tried in order until one that 257 supports your card is found. 258 ''; 259 }; 260 261 videoDriver = mkOption { 262 type = types.nullOr types.str; 263 default = null; 264 example = "i810"; 265 description = '' 266 The name of the video driver for your graphics card. This 267 option is obsolete; please set the 268 <option>services.xserver.videoDrivers</option> instead. 269 ''; 270 }; 271 272 drivers = mkOption { 273 type = types.listOf types.attrs; 274 internal = true; 275 description = '' 276 A list of attribute sets specifying drivers to be loaded by 277 the X11 server. 278 ''; 279 }; 280 281 dpi = mkOption { 282 type = types.nullOr types.int; 283 default = null; 284 description = "DPI resolution to use for X server."; 285 }; 286 287 startDbusSession = mkOption { 288 type = types.bool; 289 default = true; 290 description = '' 291 Whether to start a new DBus session when you log in with dbus-launch. 292 ''; 293 }; 294 295 updateDbusEnvironment = mkOption { 296 type = types.bool; 297 default = false; 298 description = '' 299 Whether to update the DBus activation environment after launching the 300 desktop manager. 301 ''; 302 }; 303 304 layout = mkOption { 305 type = types.str; 306 default = "us"; 307 description = '' 308 Keyboard layout, or multiple keyboard layouts separated by commas. 309 ''; 310 }; 311 312 xkbModel = mkOption { 313 type = types.str; 314 default = "pc104"; 315 example = "presario"; 316 description = '' 317 Keyboard model. 318 ''; 319 }; 320 321 xkbOptions = mkOption { 322 type = types.str; 323 default = "terminate:ctrl_alt_bksp"; 324 example = "grp:caps_toggle, grp_led:scroll"; 325 description = '' 326 X keyboard options; layout switching goes here. 327 ''; 328 }; 329 330 xkbVariant = mkOption { 331 type = types.str; 332 default = ""; 333 example = "colemak"; 334 description = '' 335 X keyboard variant. 336 ''; 337 }; 338 339 xkbDir = mkOption { 340 type = types.path; 341 description = '' 342 Path used for -xkbdir xserver parameter. 343 ''; 344 }; 345 346 config = mkOption { 347 type = types.lines; 348 description = '' 349 The contents of the configuration file of the X server 350 (<filename>xorg.conf</filename>). 351 ''; 352 }; 353 354 deviceSection = mkOption { 355 type = types.lines; 356 default = ""; 357 example = "VideoRAM 131072"; 358 description = "Contents of the first Device section of the X server configuration file."; 359 }; 360 361 screenSection = mkOption { 362 type = types.lines; 363 default = ""; 364 example = '' 365 Option "RandRRotation" "on" 366 ''; 367 description = "Contents of the first Screen section of the X server configuration file."; 368 }; 369 370 monitorSection = mkOption { 371 type = types.lines; 372 default = ""; 373 example = "HorizSync 28-49"; 374 description = "Contents of the first Monitor section of the X server configuration file."; 375 }; 376 377 xrandrHeads = mkOption { 378 default = []; 379 example = [ 380 "HDMI-0" 381 { output = "DVI-0"; primary = true; } 382 { output = "DVI-1"; monitorConfig = "Option \"Rotate\" \"left\""; } 383 ]; 384 type = with types; listOf (coercedTo str (output: { 385 inherit output; 386 }) (submodule { options = xrandrOptions; })); 387 # Set primary to true for the first head if no other has been set 388 # primary already. 389 apply = heads: let 390 hasPrimary = any (x: x.primary) heads; 391 firstPrimary = head heads // { primary = true; }; 392 newHeads = singleton firstPrimary ++ tail heads; 393 in if heads != [] && !hasPrimary then newHeads else heads; 394 description = '' 395 Multiple monitor configuration, just specify a list of XRandR 396 outputs. The individual elements should be either simple strings or 397 an attribute set of output options. 398 399 If the element is a string, it is denoting the physical output for a 400 monitor, if it's an attribute set, you must at least provide the 401 <option>output</option> option. 402 403 The monitors will be mapped from left to right in the order of the 404 list. 405 406 By default, the first monitor will be set as the primary monitor if 407 none of the elements contain an option that has set 408 <option>primary</option> to <literal>true</literal>. 409 410 <note><para>Only one monitor is allowed to be primary.</para></note> 411 412 Be careful using this option with multiple graphic adapters or with 413 drivers that have poor support for XRandR, unexpected things might 414 happen with those. 415 ''; 416 }; 417 418 serverFlagsSection = mkOption { 419 default = ""; 420 example = 421 '' 422 Option "BlankTime" "0" 423 Option "StandbyTime" "0" 424 Option "SuspendTime" "0" 425 Option "OffTime" "0" 426 ''; 427 description = "Contents of the ServerFlags section of the X server configuration file."; 428 }; 429 430 moduleSection = mkOption { 431 type = types.lines; 432 default = ""; 433 example = 434 '' 435 SubSection "extmod" 436 EndSubsection 437 ''; 438 description = "Contents of the Module section of the X server configuration file."; 439 }; 440 441 serverLayoutSection = mkOption { 442 type = types.lines; 443 default = ""; 444 example = 445 '' 446 Option "AIGLX" "true" 447 ''; 448 description = "Contents of the ServerLayout section of the X server configuration file."; 449 }; 450 451 extraDisplaySettings = mkOption { 452 type = types.lines; 453 default = ""; 454 example = "Virtual 2048 2048"; 455 description = "Lines to be added to every Display subsection of the Screen section."; 456 }; 457 458 defaultDepth = mkOption { 459 type = types.int; 460 default = 0; 461 example = 8; 462 description = "Default colour depth."; 463 }; 464 465 useXFS = mkOption { 466 # FIXME: what's the type of this option? 467 default = false; 468 example = "unix/:7100"; 469 description = "Determines how to connect to the X Font Server."; 470 }; 471 472 tty = mkOption { 473 type = types.nullOr types.int; 474 default = 7; 475 description = "Virtual console for the X server."; 476 }; 477 478 display = mkOption { 479 type = types.nullOr types.int; 480 default = 0; 481 description = "Display number for the X server."; 482 }; 483 484 virtualScreen = mkOption { 485 type = types.nullOr types.attrs; 486 default = null; 487 example = { x = 2048; y = 2048; }; 488 description = '' 489 Virtual screen size for Xrandr. 490 ''; 491 }; 492 493 verbose = mkOption { 494 type = types.nullOr types.int; 495 default = 3; 496 example = 7; 497 description = '' 498 Controls verbosity of X logging. 499 ''; 500 }; 501 502 useGlamor = mkOption { 503 type = types.bool; 504 default = false; 505 description = '' 506 Whether to use the Glamor module for 2D acceleration, 507 if possible. 508 ''; 509 }; 510 511 enableCtrlAltBackspace = mkOption { 512 type = types.bool; 513 default = false; 514 description = '' 515 Whether to enable the DontZap option, which binds Ctrl+Alt+Backspace 516 to forcefully kill X. This can lead to data loss and is disabled 517 by default. 518 ''; 519 }; 520 521 terminateOnReset = mkOption { 522 type = types.bool; 523 default = true; 524 description = '' 525 Whether to terminate X upon server reset. 526 ''; 527 }; 528 }; 529 530 }; 531 532 533 534 ###### implementation 535 536 config = mkIf cfg.enable { 537 538 services.xserver.displayManager.lightdm.enable = 539 let dmconf = cfg.displayManager; 540 default = !( dmconf.auto.enable 541 || dmconf.gdm.enable 542 || dmconf.sddm.enable 543 || dmconf.slim.enable 544 || dmconf.xpra.enable ); 545 in mkIf (default) true; 546 547 hardware.opengl.enable = mkDefault true; 548 549 services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ]; 550 551 # FIXME: somehow check for unknown driver names. 552 services.xserver.drivers = flip concatMap cfg.videoDrivers (name: 553 let driver = 554 attrByPath [name] 555 (if xorg ? ${"xf86video" + name} 556 then { modules = [xorg.${"xf86video" + name}]; } 557 else null) 558 knownVideoDrivers; 559 in optional (driver != null) ({ inherit name; modules = []; driverName = name; } // driver)); 560 561 nixpkgs.config = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { xorg.abiCompat = "1.18"; }; 562 563 assertions = [ 564 { assertion = config.security.polkit.enable; 565 message = "X11 requires Polkit to be enabled (security.polkit.enable = true)."; 566 } 567 (let primaryHeads = filter (x: x.primary) cfg.xrandrHeads; in { 568 assertion = length primaryHeads < 2; 569 message = "Only one head is allowed to be primary in " 570 + "services.xserver.xrandrHeads, but there are " 571 + "${toString (length primaryHeads)} heads set to primary: " 572 + concatMapStringsSep ", " (x: x.output) primaryHeads; 573 }) 574 ]; 575 576 environment.etc = 577 (optionals cfg.exportConfiguration 578 [ { source = "${configFile}"; 579 target = "X11/xorg.conf"; 580 } 581 # -xkbdir command line option does not seems to be passed to xkbcomp. 582 { source = "${cfg.xkbDir}"; 583 target = "X11/xkb"; 584 } 585 ]) 586 # localectl looks into 00-keyboard.conf 587 ++ [ 588 { 589 text = '' 590 Section "InputClass" 591 Identifier "Keyboard catchall" 592 MatchIsKeyboard "on" 593 Option "XkbModel" "${cfg.xkbModel}" 594 Option "XkbLayout" "${cfg.layout}" 595 Option "XkbOptions" "${cfg.xkbOptions}" 596 Option "XkbVariant" "${cfg.xkbVariant}" 597 EndSection 598 ''; 599 target = "X11/xorg.conf.d/00-keyboard.conf"; 600 } 601 ] 602 # Needed since 1.18; see https://bugs.freedesktop.org/show_bug.cgi?id=89023#c5 603 ++ (let cfgPath = "/X11/xorg.conf.d/10-evdev.conf"; in 604 [{ 605 source = xorg.xf86inputevdev.out + "/share" + cfgPath; 606 target = cfgPath; 607 }] 608 ); 609 610 environment.systemPackages = 611 [ xorg.xorgserver.out 612 xorg.xrandr 613 xorg.xrdb 614 xorg.setxkbmap 615 xorg.iceauth # required for KDE applications (it's called by dcopserver) 616 xorg.xlsclients 617 xorg.xset 618 xorg.xsetroot 619 xorg.xinput 620 xorg.xprop 621 xorg.xauth 622 pkgs.xterm 623 pkgs.xdg_utils 624 xorg.xf86inputevdev.out # get evdev.4 man page 625 ] 626 ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh; 627 628 xdg = { 629 autostart.enable = true; 630 menus.enable = true; 631 mime.enable = true; 632 icons.enable = true; 633 }; 634 635 # The default max inotify watches is 8192. 636 # Nowadays most apps require a good number of inotify watches, 637 # the value below is used by default on several other distros. 638 boot.kernel.sysctl."fs.inotify.max_user_watches" = mkDefault 524288; 639 640 systemd.defaultUnit = mkIf cfg.autorun "graphical.target"; 641 642 systemd.services.display-manager = 643 { description = "X11 Server"; 644 645 after = [ "systemd-udev-settle.service" "local-fs.target" "acpid.service" "systemd-logind.service" ]; 646 wants = [ "systemd-udev-settle.service" ]; 647 648 restartIfChanged = false; 649 650 environment = 651 { 652 LD_LIBRARY_PATH = concatStringsSep ":" ([ "/run/opengl-driver/lib" ] 653 ++ concatLists (catAttrs "libPath" cfg.drivers)); 654 } // cfg.displayManager.job.environment; 655 656 preStart = 657 '' 658 ${cfg.displayManager.job.preStart} 659 660 rm -f /tmp/.X0-lock 661 ''; 662 663 script = "${cfg.displayManager.job.execCmd}"; 664 665 serviceConfig = { 666 Restart = "always"; 667 RestartSec = "200ms"; 668 SyslogIdentifier = "display-manager"; 669 # Stop restarting if the display manager stops (crashes) 2 times 670 # in one minute. Starting X typically takes 3-4s. 671 StartLimitInterval = "30s"; 672 StartLimitBurst = "3"; 673 }; 674 }; 675 676 services.xserver.displayManager.xserverArgs = 677 [ "-config ${configFile}" 678 "-xkbdir" "${cfg.xkbDir}" 679 # Log at the default verbosity level to stderr rather than /var/log/X.*.log. 680 "-logfile" "/dev/null" 681 ] ++ optional (cfg.display != null) ":${toString cfg.display}" 682 ++ optional (cfg.tty != null) "vt${toString cfg.tty}" 683 ++ optional (cfg.dpi != null) "-dpi ${toString cfg.dpi}" 684 ++ optional (cfg.verbose != null) "-verbose ${toString cfg.verbose}" 685 ++ optional (!cfg.enableTCP) "-nolisten tcp" 686 ++ optional (cfg.autoRepeatDelay != null) "-ardelay ${toString cfg.autoRepeatDelay}" 687 ++ optional (cfg.autoRepeatInterval != null) "-arinterval ${toString cfg.autoRepeatInterval}" 688 ++ optional cfg.terminateOnReset "-terminate"; 689 690 services.xserver.modules = 691 concatLists (catAttrs "modules" cfg.drivers) ++ 692 [ xorg.xorgserver.out 693 xorg.xf86inputevdev.out 694 ]; 695 696 services.xserver.xkbDir = mkDefault "${pkgs.xkeyboard_config}/etc/X11/xkb"; 697 698 system.extraDependencies = singleton (pkgs.runCommand "xkb-validated" { 699 inherit (cfg) xkbModel layout xkbVariant xkbOptions; 700 nativeBuildInputs = [ pkgs.xkbvalidate ]; 701 } '' 702 validate "$xkbModel" "$layout" "$xkbVariant" "$xkbOptions" 703 touch "$out" 704 ''); 705 706 services.xserver.config = 707 '' 708 Section "ServerFlags" 709 Option "AllowMouseOpenFail" "on" 710 Option "DontZap" "${if cfg.enableCtrlAltBackspace then "off" else "on"}" 711 ${cfg.serverFlagsSection} 712 EndSection 713 714 Section "Module" 715 ${cfg.moduleSection} 716 EndSection 717 718 Section "Monitor" 719 Identifier "Monitor[0]" 720 ${cfg.monitorSection} 721 EndSection 722 723 # Additional "InputClass" sections 724 ${flip concatMapStrings cfg.inputClassSections (inputClassSection: '' 725 Section "InputClass" 726 ${inputClassSection} 727 EndSection 728 '')} 729 730 731 Section "ServerLayout" 732 Identifier "Layout[all]" 733 ${cfg.serverLayoutSection} 734 # Reference the Screen sections for each driver. This will 735 # cause the X server to try each in turn. 736 ${flip concatMapStrings cfg.drivers (d: '' 737 Screen "Screen-${d.name}[0]" 738 '')} 739 EndSection 740 741 ${if cfg.useGlamor then '' 742 Section "Module" 743 Load "dri2" 744 Load "glamoregl" 745 EndSection 746 '' else ""} 747 748 # For each supported driver, add a "Device" and "Screen" 749 # section. 750 ${flip concatMapStrings cfg.drivers (driver: '' 751 752 Section "Device" 753 Identifier "Device-${driver.name}[0]" 754 Driver "${driver.driverName or driver.name}" 755 ${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""} 756 ${cfg.deviceSection} 757 ${xrandrDeviceSection} 758 EndSection 759 760 Section "Screen" 761 Identifier "Screen-${driver.name}[0]" 762 Device "Device-${driver.name}[0]" 763 ${optionalString (cfg.monitorSection != "") '' 764 Monitor "Monitor[0]" 765 ''} 766 767 ${cfg.screenSection} 768 769 ${optionalString (cfg.defaultDepth != 0) '' 770 DefaultDepth ${toString cfg.defaultDepth} 771 ''} 772 773 ${optionalString 774 (driver.name != "virtualbox" && 775 (cfg.resolutions != [] || 776 cfg.extraDisplaySettings != "" || 777 cfg.virtualScreen != null)) 778 (let 779 f = depth: 780 '' 781 SubSection "Display" 782 Depth ${toString depth} 783 ${optionalString (cfg.resolutions != []) 784 "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"} 785 ${cfg.extraDisplaySettings} 786 ${optionalString (cfg.virtualScreen != null) 787 "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"} 788 EndSubSection 789 ''; 790 in concatMapStrings f [8 16 24] 791 )} 792 793 EndSection 794 '')} 795 796 ${xrandrMonitorSections} 797 ''; 798 799 fonts.enableDefaultFonts = mkDefault true; 800 801 }; 802 803}