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