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 unichrome = { modules = [ pkgs.xorgVideoUnichrome ]; };
17 virtualbox = { modules = [ kernelPackages.virtualboxGuestAdditions ]; driverName = "vboxvideo"; };
18 ati = { modules = [ pkgs.xorg.xf86videoati pkgs.xorg.glamoregl ]; };
19 intel-testing = { modules = with pkgs.xorg; [ xf86videointel-testing glamoregl ]; driverName = "intel"; };
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 = [ ''
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 description = "Content of additional InputClass sections of the X server configuration file.";
168 };
169
170 modules = mkOption {
171 type = types.listOf types.path;
172 default = [];
173 example = literalExample "[ pkgs.xf86_input_wacom ]";
174 description = "Packages to be added to the module search path of the X server.";
175 };
176
177 resolutions = mkOption {
178 type = types.listOf types.attrs;
179 default = [];
180 example = [ { x = 1600; y = 1200; } { x = 1024; y = 786; } ];
181 description = ''
182 The screen resolutions for the X server. The first element
183 is the default resolution. If this list is empty, the X
184 server will automatically configure the resolution.
185 '';
186 };
187
188 videoDrivers = mkOption {
189 type = types.listOf types.str;
190 # !!! We'd like "nv" here, but it segfaults the X server.
191 default = [ "ati" "cirrus" "intel" "vesa" "vmware" "modesetting" ];
192 example = [ "vesa" ];
193 description = ''
194 The names of the video drivers the configuration
195 supports. They will be tried in order until one that
196 supports your card is found.
197 '';
198 };
199
200 videoDriver = mkOption {
201 type = types.nullOr types.str;
202 default = null;
203 example = "i810";
204 description = ''
205 The name of the video driver for your graphics card. This
206 option is obsolete; please set the
207 <option>services.xserver.videoDrivers</option> instead.
208 '';
209 };
210
211 drivers = mkOption {
212 type = types.listOf types.attrs;
213 internal = true;
214 description = ''
215 A list of attribute sets specifying drivers to be loaded by
216 the X11 server.
217 '';
218 };
219
220 vaapiDrivers = mkOption {
221 type = types.listOf types.path;
222 default = [ ];
223 example = literalExample "[ pkgs.vaapiIntel pkgs.vaapiVdpau ]";
224 description = ''
225 Packages providing libva acceleration drivers.
226 '';
227 };
228
229 startGnuPGAgent = mkOption {
230 type = types.bool;
231 default = false;
232 description = ''
233 Whether to start the GnuPG agent when you log in. The GnuPG agent
234 remembers private keys for you so that you don't have to type in
235 passphrases every time you make an SSH connection or sign/encrypt
236 data. Use <command>ssh-add</command> to add a key to the agent.
237 '';
238 };
239
240 startDbusSession = mkOption {
241 type = types.bool;
242 default = true;
243 description = ''
244 Whether to start a new DBus session when you log in with dbus-launch.
245 '';
246 };
247
248 layout = mkOption {
249 type = types.str;
250 default = "us";
251 description = ''
252 Keyboard layout.
253 '';
254 };
255
256 xkbModel = mkOption {
257 type = types.str;
258 default = "pc104";
259 example = "presario";
260 description = ''
261 Keyboard model.
262 '';
263 };
264
265 xkbOptions = mkOption {
266 type = types.str;
267 default = "terminate:ctrl_alt_bksp";
268 example = "grp:caps_toggle, grp_led:scroll";
269 description = ''
270 X keyboard options; layout switching goes here.
271 '';
272 };
273
274 xkbVariant = mkOption {
275 type = types.str;
276 default = "";
277 example = "colemak";
278 description = ''
279 X keyboard variant.
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.int;
385 default = 7;
386 description = "Virtual console for the X server.";
387 };
388
389 display = mkOption {
390 type = 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
414 };
415
416
417
418 ###### implementation
419
420 config = mkIf cfg.enable {
421
422 hardware.opengl.enable = mkDefault true;
423
424 services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];
425
426 # FIXME: somehow check for unknown driver names.
427 services.xserver.drivers = flip concatMap cfg.videoDrivers (name:
428 let driver =
429 attrByPath [name]
430 (if xorg ? ${"xf86video" + name}
431 then { modules = [xorg.${"xf86video" + name}]; }
432 else null)
433 knownVideoDrivers;
434 in optional (driver != null) ({ inherit name; driverName = name; } // driver));
435
436 assertions =
437 [ { assertion = !(config.programs.ssh.startAgent && cfg.startGnuPGAgent);
438 message =
439 ''
440 The OpenSSH agent and GnuPG agent cannot be started both. Please
441 choose between ‘programs.ssh.startAgent’ and ‘services.xserver.startGnuPGAgent’.
442 '';
443 }
444 { assertion = config.security.polkit.enable;
445 message = "X11 requires Polkit to be enabled (‘security.polkit.enable = true’).";
446 }
447 ];
448
449 environment.etc =
450 (optionals cfg.exportConfiguration
451 [ { source = "${configFile}";
452 target = "X11/xorg.conf";
453 }
454 # -xkbdir command line option does not seems to be passed to xkbcomp.
455 { source = "${pkgs.xkeyboard_config}/etc/X11/xkb";
456 target = "X11/xkb";
457 }
458 ]);
459
460 environment.systemPackages =
461 [ xorg.xorgserver
462 xorg.xrandr
463 xorg.xrdb
464 xorg.setxkbmap
465 xorg.iceauth # required for KDE applications (it's called by dcopserver)
466 xorg.xlsclients
467 xorg.xset
468 xorg.xsetroot
469 xorg.xinput
470 xorg.xprop
471 pkgs.xterm
472 pkgs.xdg_utils
473 ]
474 ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh;
475
476 environment.pathsToLink =
477 [ "/etc/xdg" "/share/xdg" "/share/applications" "/share/icons" "/share/pixmaps" ];
478
479 # The default max inotify watches is 8192.
480 # Nowadays most apps require a good number of inotify watches,
481 # the value below is used by default on several other distros.
482 boot.kernel.sysctl."fs.inotify.max_user_watches" = mkDefault 524288;
483
484 systemd.defaultUnit = mkIf cfg.autorun "graphical.target";
485
486 systemd.services.display-manager =
487 { description = "X11 Server";
488
489 after = [ "systemd-udev-settle.service" "local-fs.target" "acpid.service" ];
490
491 restartIfChanged = false;
492
493 environment =
494 {
495 XKB_BINDIR = "${xorg.xkbcomp}/bin"; # Needed for the Xkb extension.
496 XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime.
497 LD_LIBRARY_PATH = concatStringsSep ":" (
498 [ "${xorg.libX11}/lib" "${xorg.libXext}/lib" ]
499 ++ concatLists (catAttrs "libPath" cfg.drivers));
500 } // cfg.displayManager.job.environment;
501
502 preStart =
503 ''
504 ${cfg.displayManager.job.preStart}
505
506 rm -f /tmp/.X0-lock
507 '';
508
509 script = "${cfg.displayManager.job.execCmd}";
510
511 serviceConfig = {
512 Restart = "always";
513 RestartSec = "200ms";
514 };
515 };
516
517 services.xserver.displayManager.xserverArgs =
518 [ "-ac"
519 "-terminate"
520 "-logfile" "/var/log/X.${toString cfg.display}.log"
521 "-config ${configFile}"
522 ":${toString cfg.display}" "vt${toString cfg.tty}"
523 "-xkbdir" "${pkgs.xkeyboard_config}/etc/X11/xkb"
524 ] ++ optional (!cfg.enableTCP) "-nolisten tcp";
525
526 services.xserver.modules =
527 concatLists (catAttrs "modules" cfg.drivers) ++
528 [ xorg.xorgserver
529 xorg.xf86inputevdev
530 ];
531
532 services.xserver.config =
533 ''
534 Section "ServerFlags"
535 Option "AllowMouseOpenFail" "on"
536 ${cfg.serverFlagsSection}
537 EndSection
538
539 Section "Module"
540 ${cfg.moduleSection}
541 EndSection
542
543 Section "Monitor"
544 Identifier "Monitor[0]"
545 ${cfg.monitorSection}
546 EndSection
547
548 Section "InputClass"
549 Identifier "Keyboard catchall"
550 MatchIsKeyboard "on"
551 Option "XkbRules" "base"
552 Option "XkbModel" "${cfg.xkbModel}"
553 Option "XkbLayout" "${cfg.layout}"
554 Option "XkbOptions" "${cfg.xkbOptions}"
555 Option "XkbVariant" "${cfg.xkbVariant}"
556 EndSection
557
558 # Additional "InputClass" sections
559 ${flip concatMapStrings cfg.inputClassSections (inputClassSection: ''
560 Section "InputClass"
561 ${inputClassSection}
562 EndSection
563 '')}
564
565
566 Section "ServerLayout"
567 Identifier "Layout[all]"
568 ${cfg.serverLayoutSection}
569 # Reference the Screen sections for each driver. This will
570 # cause the X server to try each in turn.
571 ${flip concatMapStrings cfg.drivers (d: ''
572 Screen "Screen-${d.name}[0]"
573 '')}
574 EndSection
575
576 ${if cfg.useGlamor then ''
577 Section "Module"
578 Load "dri2"
579 Load "glamoregl"
580 EndSection
581 '' else ""}
582
583 # For each supported driver, add a "Device" and "Screen"
584 # section.
585 ${flip concatMapStrings cfg.drivers (driver: ''
586
587 Section "Device"
588 Identifier "Device-${driver.name}[0]"
589 Driver "${driver.driverName or driver.name}"
590 ${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""}
591 ${cfg.deviceSection}
592 ${xrandrDeviceSection}
593 EndSection
594
595 Section "Screen"
596 Identifier "Screen-${driver.name}[0]"
597 Device "Device-${driver.name}[0]"
598 ${optionalString (cfg.monitorSection != "") ''
599 Monitor "Monitor[0]"
600 ''}
601
602 ${cfg.screenSection}
603
604 ${optionalString (cfg.defaultDepth != 0) ''
605 DefaultDepth ${toString cfg.defaultDepth}
606 ''}
607
608 ${optionalString
609 (driver.name != "virtualbox" &&
610 (cfg.resolutions != [] ||
611 cfg.extraDisplaySettings != "" ||
612 cfg.virtualScreen != null))
613 (let
614 f = depth:
615 ''
616 SubSection "Display"
617 Depth ${toString depth}
618 ${optionalString (cfg.resolutions != [])
619 "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"}
620 ${cfg.extraDisplaySettings}
621 ${optionalString (cfg.virtualScreen != null)
622 "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"}
623 EndSubSection
624 '';
625 in concatMapStrings f [8 16 24]
626 )}
627
628 EndSection
629 '')}
630
631 ${xrandrMonitorSections}
632 '';
633
634 };
635
636}