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}