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