summary refs log tree commit diff
path: root/pkgs/servers/x11/xquartz
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/servers/x11/xquartz')
-rwxr-xr-xpkgs/servers/x11/xquartz/X1120
-rw-r--r--pkgs/servers/x11/xquartz/default.nix187
-rwxr-xr-xpkgs/servers/x11/xquartz/font_cache240
-rw-r--r--pkgs/servers/x11/xquartz/org.nixos.xquartz.privileged_startx.plist23
-rw-r--r--pkgs/servers/x11/xquartz/org.nixos.xquartz.startx.plist27
-rw-r--r--pkgs/servers/x11/xquartz/patch_plist.rb47
-rwxr-xr-xpkgs/servers/x11/xquartz/privileged43
-rwxr-xr-xpkgs/servers/x11/xquartz/startx232
-rw-r--r--pkgs/servers/x11/xquartz/system-fonts.nix36
-rwxr-xr-xpkgs/servers/x11/xquartz/xinitrc40
10 files changed, 895 insertions, 0 deletions
diff --git a/pkgs/servers/x11/xquartz/X11 b/pkgs/servers/x11/xquartz/X11
new file mode 100755
index 00000000000..39bcce2c10b
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/X11
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set "$(dirname "$0")"/X11.bin "${@}"
+
+export XQUARTZ_DEFAULT_CLIENT="@DEFAULT_CLIENT@"
+export XQUARTZ_DEFAULT_SHELL="@DEFAULT_SHELL@"
+export XQUARTZ_DEFAULT_STARTX="@DEFAULT_STARTX@"
+export FONTCONFIG_FILE="@FONTCONFIG_FILE@"
+
+if [ -x ~/.x11run ]; then
+    exec ~/.x11run "${@}"
+fi
+
+case $(basename "${SHELL}") in
+    bash)          exec -l "${SHELL}" --login -c 'exec "${@}"' - "${@}" ;;
+    ksh|sh|zsh)    exec -l "${SHELL}" -c 'exec "${@}"' - "${@}" ;;
+    csh|tcsh)      exec -l "${SHELL}" -c 'exec $argv:q' "${@}" ;;
+    es|rc)         exec -l "${SHELL}" -l -c 'exec $*' "${@}" ;;
+    *)             exec    "${@}" ;;
+esac
diff --git a/pkgs/servers/x11/xquartz/default.nix b/pkgs/servers/x11/xquartz/default.nix
new file mode 100644
index 00000000000..abcae211f42
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/default.nix
@@ -0,0 +1,187 @@
+{ stdenv, lib, buildEnv, makeFontsConf, gnused, writeScript, xorg, bashInteractive, substituteAll, xterm, makeWrapper, ruby
+, openssl, quartz-wm, fontconfig, xkeyboard_config, xlsfonts, xfontsel
+, ttf_bitstream_vera, freefont_ttf, liberation_ttf
+, shell ? "${bashInteractive}/bin/bash"
+}:
+
+# ------------
+# Installation
+# ------------
+#
+# First, assuming you've previously installed XQuartz from macosforge.com,
+# unload and remove the existing launch agents:
+#
+#   $ sudo launchctl unload /Library/LaunchAgents/org.macosforge.xquartz.startx.plist
+#   $ sudo launchctl unload /Library/LaunchDaemons/org.macosforge.xquartz.privileged_startx.plist
+#   $ sudo rm /Library/LaunchAgents/org.macosforge.xquartz.startx.plist
+#   $ sudo rm /Library/LaunchDaemons/org.macosforge.xquartz.privileged_startx.plist
+#
+# (You will need to log out for the above changes to take effect.)
+#
+# Then install xquartz from nixpkgs:
+#
+#   $ nix-env -i xquartz
+#   $ xquartz-install
+#
+# You'll also want to add the following to your shell's profile (after you
+# source nix.sh, so $NIX_LINK points to your user profile):
+#
+#   if [ "$(uname)" = "Darwin" -a -n "$NIX_LINK" -a -f $NIX_LINK/etc/X11/fonts.conf ]; then
+#     export FONTCONFIG_FILE=$NIX_LINK/etc/X11/fonts.conf
+#   fi
+
+# A note about dependencies:
+# Xquartz wants to exec XQuartz.app, XQuartz.app wants to exec xstart, and
+# xstart wants to exec Xquartz, so we must bundle all three to prevent a cycle.
+# Coincidentally, this also makes it trivial to install launch agents/daemons
+# that point into the user's profile.
+
+let
+  shellEscape = x: "'${lib.replaceChars ["'"] [("'\\'" + "'")] x}'";
+  installer = writeScript "xquartz-install" ''
+    NIX_LINK=$HOME/.nix-profile
+
+    tmpdir=$(/usr/bin/mktemp -d $TMPDIR/xquartz-installer-XXXXXXXX)
+    agentName=org.nixos.xquartz.startx.plist
+    daemonName=org.nixos.xquartz.privileged_startx.plist
+    sed=${gnused}/bin/sed
+
+    cp ${./org.nixos.xquartz.startx.plist} $tmpdir/$agentName
+    $sed -i "s|@LAUNCHD_STARTX@|$NIX_LINK/etc/X11/xinit/launchd_startx|" $tmpdir/$agentName
+    $sed -i "s|@STARTX@|$NIX_LINK/bin/startx|" $tmpdir/$agentName
+    $sed -i "s|@XQUARTZ@|$NIX_LINK/bin/Xquartz|" $tmpdir/$agentName
+
+    cp ${./org.nixos.xquartz.privileged_startx.plist} $tmpdir/$daemonName
+    $sed -i "s|@PRIVILEGED_STARTX@|$NIX_LINK/lib/X11/xinit/privileged_startx|" $tmpdir/$daemonName
+    $sed -i "s|@PRIVILEGED_STARTX_D@|$NIX_LINK/lib/X11/xinit/privileged_startx.d|" $tmpdir/$daemonName
+
+    sudo cp $tmpdir/$agentName /Library/LaunchAgents/$agentName
+    sudo cp $tmpdir/$daemonName /Library/LaunchDaemons/$daemonName
+    sudo launchctl load -w /Library/LaunchAgents/$agentName
+    sudo launchctl load -w /Library/LaunchDaemons/$daemonName
+  '';
+  fontDirs = [
+    xorg.fontbhttf
+    xorg.fontbhlucidatypewriter100dpi
+    xorg.fontbhlucidatypewriter75dpi
+    ttf_bitstream_vera
+    freefont_ttf
+    liberation_ttf
+    xorg.fontbh100dpi
+    xorg.fontmiscmisc
+    xorg.fontcursormisc
+  ];
+  fontsConf = makeFontsConf {
+    fontDirectories = fontDirs ++ [
+      "/Library/Fonts"
+      "~/Library/Fonts"
+    ];
+  };
+  fonts = import ./system-fonts.nix {
+    inherit stdenv xorg fontDirs;
+  };
+  # any X related programs expected to be available via $PATH
+  env = buildEnv {
+    name = "xquartz-env";
+    pathsToLink = [ "/bin" ];
+    paths = with xorg; [
+      # non-xorg
+      quartz-wm xterm fontconfig
+      # xorg
+      xlsfonts xfontsel
+      bdftopcf fontutil iceauth libXpm lndir luit makedepend mkfontdir
+      mkfontscale sessreg setxkbmap smproxy twm x11perf xauth xbacklight xclock
+      xcmsdb xcursorgen xdm xdpyinfo xdriinfo xev xeyes xfs xgamma xhost
+      xinput xkbcomp xkbevd xkbutils xkill xlsatoms xlsclients xmessage xmodmap
+      xpr xprop xrandr xrdb xrefresh xset xsetroot xvinfo xwd xwininfo xwud
+    ];
+  };
+in stdenv.mkDerivation {
+  name = "xquartz";
+  buildInputs = [ ruby makeWrapper ];
+  unpackPhase = "sourceRoot=.";
+  buildPhase = ":";
+  installPhase = ''
+    cp -rT ${xorg.xinit} $out
+    chmod -R u+w $out
+    cp -rT ${xorg.xorgserver} $out
+    chmod -R u+w $out
+
+    cp ${installer} $out/bin/xquartz-install
+
+    rm -r $out/LaunchAgents
+
+    fontsConfPath=$out/etc/X11/fonts.conf
+    cp ${fontsConf} $fontsConfPath
+
+    cp ${./startx} $out/bin/startx
+    substituteInPlace $out/bin/startx \
+      --replace "@PATH@"              "$out/bin:${env}" \
+      --replace "@XAUTH@"             "${xorg.xauth}/bin/xauth" \
+      --replace "@FONT_CACHE@"        "$out/bin/font_cache" \
+      --replace "@PRIVILEGED_STARTX@" "$out/lib/X11/xinit/privileged_startx" \
+      --replace "@DEFAULT_SERVER@"    "$out/bin/Xquartz" \
+      --replace "@DEFAULT_CLIENT@"    "${xterm}/bin/xterm" \
+      --replace "@XINIT@"             "$out/bin/xinit" \
+      --replace "@XINITRC@"           "$out/etc/X11/xinit/xinitrc" \
+      --replace "@XKEYBOARD_CONFIG@"  "${xkeyboard_config}/etc/X11/xkb" \
+      --replace "@FONTCONFIG_FILE@"   "$fontsConfPath"
+
+    wrapProgram $out/bin/Xquartz \
+      --set XQUARTZ_X11 $out/Applications/XQuartz.app/Contents/MacOS/X11 \
+      --set XKB_BINDIR "${xorg.xkbcomp}/bin"
+
+    defaultStartX="$out/bin/startx -- $out/bin/Xquartz"
+
+    ruby ${./patch_plist.rb} \
+      ${shellEscape (builtins.toXML {
+        XQUARTZ_DEFAULT_CLIENT = "${xterm}/bin/xterm";
+        XQUARTZ_DEFAULT_SHELL  = "${shell}";
+        XQUARTZ_DEFAULT_STARTX = "@STARTX@";
+        FONTCONFIG_FILE        = "@FONTCONFIG_FILE@";
+        XKB_BINDIR             = "${xorg.xkbcomp}/bin";
+      })} \
+      $out/Applications/XQuartz.app/Contents/Info.plist
+    substituteInPlace $out/Applications/XQuartz.app/Contents/Info.plist \
+      --replace "@STARTX@"          "$defaultStartX" \
+      --replace "@FONTCONFIG_FILE@" "$fontsConfPath"
+
+    rm $out/lib/X11/xinit/privileged_startx.d/*
+    cp ${./privileged} $out/lib/X11/xinit/privileged_startx.d/privileged
+    substituteInPlace $out/lib/X11/xinit/privileged_startx.d/privileged \
+      --replace "@PATH@"            "$out/bin:${env}" \
+      --replace "@FONTCONFIG_FILE@" "$fontsConfPath" \
+      --replace "@FONT_CACHE@"      "$out/bin/font_cache"
+
+    cp ${./font_cache} $out/bin/font_cache
+    substituteInPlace $out/bin/font_cache \
+      --replace "@PATH@"            "$out/bin:${env}" \
+      --replace "@ENCODINGSDIR@"    "${xorg.encodings}/share/fonts/X11/encodings" \
+      --replace "@MKFONTDIR@"       "${xorg.mkfontdir}/bin/mkfontdir" \
+      --replace "@MKFONTSCALE@"     "${xorg.mkfontscale}/bin/mkfontscale" \
+      --replace "@FC_CACHE@"        "${fontconfig}/bin/fc-cache" \
+      --replace "@FONTCONFIG_FILE@" "$fontsConfPath"
+
+    cp ${./xinitrc} $out/etc/X11/xinit/xinitrc
+    substituteInPlace $out/etc/X11/xinit/xinitrc \
+      --replace "@PATH@"            "$out/bin:${env}" \
+      --replace "@XSET@"            "${xorg.xset}/bin/xset" \
+      --replace "@XMODMAP@"         "${xorg.xmodmap}/bin/xmodmap" \
+      --replace "@XRDB@"            "${xorg.xrdb}/bin/xrdb" \
+      --replace "@SYSTEM_FONTS@"    "${fonts}/share/X11-fonts/" \
+      --replace "@QUARTZ_WM@"       "${quartz-wm}/bin/quartz-wm" \
+      --replace "@FONTCONFIG_FILE@" "$fontsConfPath"
+
+    cp ${./X11} $out/Applications/XQuartz.app/Contents/MacOS/X11
+    substituteInPlace $out/Applications/XQuartz.app/Contents/MacOS/X11 \
+      --replace "@DEFAULT_SHELL@"   "${shell}" \
+      --replace "@DEFAULT_STARTX@"  "$defaultStartX" \
+      --replace "@DEFAULT_CLIENT@"  "${xterm}/bin/xterm" \
+      --replace "@FONTCONFIG_FILE@" "$fontsConfPath"
+  '';
+  meta = with lib; {
+    platforms   = platforms.darwin;
+    maintainers = with maintainers; [ cstrahan ];
+    license     = licenses.mit;
+  };
+}
diff --git a/pkgs/servers/x11/xquartz/font_cache b/pkgs/servers/x11/xquartz/font_cache
new file mode 100755
index 00000000000..12858af8bc6
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/font_cache
@@ -0,0 +1,240 @@
+#!/bin/bash
+
+export PATH=@PATH@:$PATH
+
+export FONTCONFIG_FILE="@FONTCONFIG_FILE@"
+ENCODINGSDIR="@ENCODINGSDIR@"
+FC_LOCKFILE=""
+
+# Are we caching system fonts or user fonts?
+system=0
+
+# Are we including OSX font dirs ({/,~/,/System/}Library/Fonts)
+osxfonts=1
+
+# Do we want to force a recache?
+force=0
+
+# How noisy are we?
+verbose=0
+
+# Check if the data in the given directory is newer than its cache
+check_dirty() {
+    local dir=$1
+    local fontfiles=""
+    local retval=1
+
+    # If the dir does not exist, we just exit
+    if [[ ! -d "${dir}" ]]; then
+        return 1
+    fi
+
+    # Create a list of all files in the dir
+    # Filter out config / cache files.  Ugly... counting down the day until
+    # xfs finally goes away
+    fontfiles="$(find ${dir}/ -maxdepth 1 -type f | awk '$0 !~ /fonts\..*$|^.*\.dir$/ {print}')"
+
+    # Fonts were deleted (or never there).  Kill off the caches
+    if [[ -z "${fontfiles}" ]] ; then
+        local f
+        for f in "${dir}"/fonts.* "${dir}"/encodings.dir; do
+            if [[ -f ${f} ]] ; then
+                rm -f "${f}"
+            fi
+        done
+        return 1
+    fi
+
+    # Force a recache
+    if [[ ${force} == 1 ]] ; then
+        retval=0
+    fi
+
+    # If we don't have our caches, we are dirty
+    if [[ ! -f "${dir}/fonts.list" || ! -f "${dir}/fonts.dir" || ! -f "${dir}/encodings.dir" ]]; then
+        retval=0
+    fi
+
+    # Check that no files were added or removed....
+    if [[ "${retval}" -ne 0 && "$(cat ${dir}/fonts.list)" != "${fontfiles}" ]] ; then
+        retval=0
+    fi
+
+    # Check that no files were updated....
+    if [[ "${retval}" -ne 0 ]] ; then
+        local changed="$(find ${dir}/ -type f -cnewer ${dir}/fonts.dir | awk '$0 !~ /fonts\..*$|^.*\.dir$/ {print}')"
+
+        if [[ -n "${changed}" ]] ; then
+            retval=0
+        fi
+    fi
+
+    # Recreate fonts.list since something changed
+    if [[ "${retval}" == 0 ]] ; then
+        echo "${fontfiles}" > "${dir}"/fonts.list
+    fi
+
+    return ${retval}
+}
+
+get_fontdirs() {
+    local d
+    if [[ $system == 1 ]] ; then
+        if [[ $osxfonts == 1 ]] ; then
+            find {/System/,/}Library/Fonts -type d
+        fi
+    else 
+        if [[ $osxfonts == 1 && -d "${HOME}/Library/Fonts" ]] ; then
+            find "${HOME}/Library/Fonts" -type d
+        fi
+
+        if [[ -d "${HOME}/.fonts" ]] ; then
+            find "${HOME}/.fonts" -type d
+        fi
+    fi
+}
+
+setup_fontdirs() {
+    local x=""
+    local fontdirs=""
+    local changed="no"
+
+    umask 022
+
+    if [[ $system == 1 ]] ; then
+        echo "font_cache: Scanning system font directories to generate X11 font caches"
+    else
+        echo "font_cache: Scanning user font directories to generate X11 font caches"
+    fi
+
+    OIFS=$IFS
+    IFS='
+'
+    for x in $(get_fontdirs) ; do
+        if [[ -d "${x}" ]] && check_dirty "${x}" ; then
+            if [[ -z "${fontdirs}" ]] ; then
+                fontdirs="${x}"
+            else
+                fontdirs="${fontdirs}${IFS}${x}"
+            fi
+        fi
+    done
+
+    if [[ -n "${fontdirs}" ]] ; then
+        echo "font_cache: Making fonts.dir for updated directories."
+        for x in ${fontdirs} ; do
+            if [[ $verbose == 1 ]] ; then
+                echo "font_cache:    ${x}"
+            fi
+
+            # First, generate fonts.scale for scaleable fonts that might be there
+            @MKFONTSCALE@ \
+                -a $ENCODINGSDIR/encodings.dir \
+                -a $ENCODINGSDIR/large/encodings.dir \
+                -- ${x}
+
+            # Next, generate fonts.dir
+            if [[ $verbose == 1 ]] ; then
+                @MKFONTDIR@ \
+                    -e $ENCODINGSDIR \
+                    -e $ENCODINGSDIR/large \
+                    -- ${x}
+            else
+                @MKFONTDIR@ \
+                    -e $ENCODINGSDIR \
+                    -e $ENCODINGSDIR/large \
+                    -- ${x} > /dev/null
+            fi
+        done
+    fi
+    IFS=$OIFS
+
+    # Finally, update fontconfig's cache
+    echo "font_cache: Updating FC cache"
+    if [[ $system == 1 ]] ; then
+        @FC_CACHE@ -s \
+            $([[ $force == 1 ]] && echo "-f -r") \
+            $([[ $verbose == 1 ]] && echo "-v")
+    else
+        @FC_CACHE@ \
+            $([[ $force == 1 ]] && echo "-f -r") \
+            $([[ $verbose == 1 ]] && echo "-v")
+    fi
+    echo "font_cache: Done"
+}
+
+do_usage() {
+    echo "font_cache [options]"
+    echo "    -f, --force        : Force cache recreation"
+    echo "    -n, --no-osxfonts  : Just cache X11 font directories"
+    echo "                         (-n just pertains to XFont cache, not fontconfig)"
+    echo "    -s, --system       : Cache system font dirs instead of user dirs"
+    echo "    -v, --verbose      : Verbose Output"
+}
+
+cleanup() {
+    [[ -r "${FC_LOCKFILE}" ]] && rm -f "${FC_LOCKFILE}"
+    exit 1
+}
+
+while [[ $# -gt 0 ]] ; do
+    case $1 in
+        -s|--system) system=1 ;;
+        -f|--force) force=1 ;;
+        -v|--verbose) verbose=1 ;;
+        -n|--no-osxfonts) osxfonts=0 ;;
+        --help) do_usage ; exit 0 ;;
+        *) do_usage ; exit 1 ;;
+    esac
+    shift
+done
+
+if [[ $system == 1 ]] ; then
+    FC_LOCKFILE="/tmp/font_cache.$UID.lock"
+elif [[ -w "${TMPDIR}" ]] ; then
+    FC_LOCKFILE="${TMPDIR}/font_cache.lock"
+elif [[ -w "/tmp" ]] ; then
+    FC_LOCKFILE="/tmp/font_cache.$UID.lock"
+else
+    FC_LOCKFILE="${HOME}/.font_cache.lock"
+fi
+
+if [[ -x /usr/bin/lockfile ]] ; then
+    if /usr/bin/lockfile -r 0 -l 240 -s 4 -! "${FC_LOCKFILE}" ; then
+            echo "font_cache is already running." >&2
+            echo "If you believe this to be erroneous, please remove ${FC_LOCKFILE}." >&2
+            exit 1
+    fi
+else
+    if [[ -r "${FC_LOCKFILE}" ]] ; then
+        read OLD_PID < "${FC_LOCKFILE}"
+        if kill -0 ${OLD_PID} >& /dev/null ; then
+            echo "font_cache is already running with PID ${OLD_PID}." >&2
+            echo "If you believe this to be erroneous, please remove ${FC_LOCKFILE}." >&2
+            exit 1
+        fi
+
+        echo "Removing stale ${FC_LOCKFILE}" >&2
+        rm -f "${FC_LOCKFILE}"
+    fi
+
+    echo $$ > "${FC_LOCKFILE}"
+
+    if [[ ! -r "${FC_LOCKFILE}" ]] ; then
+        echo "Unable to write to ${FC_LOCKFILE}." >&2
+        exit 1
+    fi
+
+    # Now make sure we didn't collide mid-air
+    read OLD_PID < "${FC_LOCKFILE}"
+    if [[ $$ != ${OLD_PID} ]] ; then
+        echo "font_cache is already running with PID ${OLD_PID}." >&2
+        exit 1
+    fi
+fi
+
+trap cleanup SIGINT SIGQUIT SIGABRT SIGTERM
+
+setup_fontdirs
+
+rm -f "${FC_LOCKFILE}"
diff --git a/pkgs/servers/x11/xquartz/org.nixos.xquartz.privileged_startx.plist b/pkgs/servers/x11/xquartz/org.nixos.xquartz.privileged_startx.plist
new file mode 100644
index 00000000000..190b383676d
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/org.nixos.xquartz.privileged_startx.plist
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+    <key>Label</key>
+    <string>org.nixos.xquartz.privileged_startx</string>
+    <key>ProgramArguments</key>
+    <array>
+      <string>@PRIVILEGED_STARTX@</string>
+      <string>-d</string>
+      <string>@PRIVILEGED_STARTX_D@</string>
+    </array>
+    <key>MachServices</key>
+    <dict>
+      <key>org.nixos.xquartz.privileged_startx</key>
+      <true/>
+    </dict>
+    <key>TimeOut</key>
+    <integer>120</integer>
+    <key>EnableTransactions</key>
+    <true/>
+  </dict>
+</plist>
diff --git a/pkgs/servers/x11/xquartz/org.nixos.xquartz.startx.plist b/pkgs/servers/x11/xquartz/org.nixos.xquartz.startx.plist
new file mode 100644
index 00000000000..7a95916da16
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/org.nixos.xquartz.startx.plist
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+    <key>Label</key>
+    <string>org.nixos.xquartz.startx</string>
+    <key>ProgramArguments</key>
+    <array>
+      <string>@LAUNCHD_STARTX@</string>
+      <string>@STARTX@</string>
+      <string>--</string>
+      <string>@XQUARTZ@</string>
+    </array>
+    <key>Sockets</key>
+    <dict>
+      <key>org.nixos.xquartz:0</key>
+      <dict>
+        <key>SecureSocketWithKey</key>
+        <string>DISPLAY</string>
+      </dict>
+    </dict>
+    <key>ServiceIPC</key>
+    <true/>
+    <key>EnableTransactions</key>
+    <true/>
+  </dict>
+</plist>
diff --git a/pkgs/servers/x11/xquartz/patch_plist.rb b/pkgs/servers/x11/xquartz/patch_plist.rb
new file mode 100644
index 00000000000..9b0f778426e
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/patch_plist.rb
@@ -0,0 +1,47 @@
+require 'rexml/document'
+
+# This script is for setting environment variables in OSX applications.
+# 
+# This script takes two arguments:
+# 1) A Nix attrset serialized via `builtins.toXML'
+# 2) The path to an OSX app's Info.plist file.
+
+def main(serialized_attrs, plist_path)
+  env          = attrs_to_hash(serialized_attrs)
+  doc          = REXML::Document.new(File.open(plist_path, &:read))
+  topmost_dict = doc.root.elements.detect { |e| e.name == "dict" }
+  topmost_dict.add_element("key").tap do |key|
+    key.text = "LSEnvironment"
+  end
+  topmost_dict.add_element(env_to_dict(env))
+
+  formatter = REXML::Formatters::Pretty.new(2)
+  formatter.compact = true
+  formatter.write(doc, File.open(plist_path, "w"))
+end
+
+# Convert a `builtins.toXML' serialized attrs to a hash.
+# This assumes the values are strings.
+def attrs_to_hash(serialized_attrs)
+  hash = {}
+  env_vars = REXML::Document.new(serialized_attrs)
+  env_vars.root.elements[1].elements.each do |attr|
+    name = attr.attribute("name")
+    value = attr.elements.first.attribute("value")
+    hash[name] = value
+  end
+  hash
+end
+
+def env_to_dict(env)
+  dict = REXML::Element.new("dict")
+  env.each do |k, v|
+    key = dict.add_element("key")
+    key.text = k
+    string = dict.add_element("string")
+    string.text = v
+  end
+  dict
+end
+
+main(ARGV[0], ARGV[1])
diff --git a/pkgs/servers/x11/xquartz/privileged b/pkgs/servers/x11/xquartz/privileged
new file mode 100755
index 00000000000..3eee9479411
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/privileged
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+export PATH=@PATH@:$PATH
+export FONTCONFIG_FILE="@FONTCONFIG_FILE@"
+
+# Our usage of mktemp fails with GNU, so prefer /usr/bin to hopefully
+# get BSD mktemp
+if [ -x /usr/bin/mktemp ] ; then
+    MKTEMP=/usr/bin/mktemp
+else
+    MKTEMP=mktemp
+fi
+
+STAT=/usr/bin/stat
+
+for dir in /tmp/.ICE-unix /tmp/.X11-unix /tmp/.font-unix ; do
+    success=0
+    for attempt in 1 2 3 4 5 ; do
+        check=`${STAT} -f '%#p %u %g' ${dir} 2> /dev/null`
+        if [ "${check}" = "041777 0 0" ] ; then
+            success=1
+            break
+        elif [ -n "${check}" ] ; then
+            saved=$(${MKTEMP} -d ${dir}-XXXXXXXX)
+            mv ${dir} ${saved}
+            echo "${dir} exists but is insecure.  It has been moved into ${saved}" >&2
+        fi
+
+        # if $dir exists and is a symlink (ie protect against a race)
+        if ${MKTEMP} -d ${dir} >& /dev/null ; then
+            chmod 1777 $dir
+            chown root:wheel $dir
+            success=1
+            break
+        fi
+    done
+
+    if [ "${success}" -eq 0 ] ; then
+        echo "Could not successfully create ${dir}" >&2
+    fi
+done
+
+@FONT_CACHE@ -s &
diff --git a/pkgs/servers/x11/xquartz/startx b/pkgs/servers/x11/xquartz/startx
new file mode 100755
index 00000000000..131fbc43b8b
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/startx
@@ -0,0 +1,232 @@
+#!/bin/sh
+
+# vim :set ts=4 sw=4 sts=4 et :
+
+#
+# This is just a sample implementation of a slightly less primitive
+# interface than xinit.  It looks for user .xinitrc and .xserverrc
+# files, then system xinitrc and xserverrc files, else lets xinit choose
+# its default.  The system xinitrc should probably do things like check
+# for .Xresources files and merge them in, start up a window manager,
+# and pop a clock and several xterms.
+#
+# Site administrators are STRONGLY urged to write nicer versions.
+#
+
+unset DBUS_SESSION_BUS_ADDRESS
+unset SESSION_MANAGER
+
+
+# Bourne shell does not automatically export modified environment variables
+# so export the new PATH just in case the user changes the shell
+export PATH=@PATH@:$PATH
+
+export FONTCONFIG_FILE="@FONTCONFIG_FILE@"
+
+userclientrc=$HOME/.xinitrc
+sysclientrc=@XINITRC@
+
+userserverrc=$HOME/.xserverrc
+sysserverrc=@XINITRC@
+defaultclient=@DEFAULT_CLIENT@ # xterm
+defaultserver=@DEFAULT_SERVER@
+defaultclientargs=""
+defaultserverargs=""
+defaultdisplay=":0"
+clientargs=""
+serverargs=""
+
+export X11_PREFS_DOMAIN=org.nixos.xquartz".X11"
+
+# Initialize defaults (this will cut down on "safe" error messages)
+if ! /usr/bin/defaults read $X11_PREFS_DOMAIN cache_fonts > /dev/null 2>&1 ; then
+    /usr/bin/defaults write $X11_PREFS_DOMAIN cache_fonts -bool true
+fi
+
+if ! /usr/bin/defaults read $X11_PREFS_DOMAIN no_auth > /dev/null 2>&1 ; then
+    /usr/bin/defaults write $X11_PREFS_DOMAIN no_auth -bool false
+fi
+
+if ! /usr/bin/defaults read $X11_PREFS_DOMAIN nolisten_tcp > /dev/null 2>&1 ; then
+    /usr/bin/defaults write $X11_PREFS_DOMAIN nolisten_tcp -bool true
+fi
+
+# First, start caching fonts
+if [ x`/usr/bin/defaults read $X11_PREFS_DOMAIN cache_fonts` = x1 ] ; then
+    @FONT_CACHE@ &
+fi
+
+# a race to create /tmp/.X11-unix
+@PRIVILEGED_STARTX@
+
+if [ x`/usr/bin/defaults read $X11_PREFS_DOMAIN no_auth` = x0 ] ; then
+    enable_xauth=1
+else
+    enable_xauth=0
+fi
+
+if [ x`defaults read $X11_PREFS_DOMAIN nolisten_tcp` = x1 ] ; then
+    defaultserverargs="$defaultserverargs -nolisten tcp"
+fi
+
+# The second check is the real one.  The first is to hopefully avoid
+# needless syslog spamming.
+if /usr/bin/defaults read $X11_PREFS_DOMAIN 2> /dev/null | grep -q 'dpi' && /usr/bin/defaults read $X11_PREFS_DOMAIN dpi > /dev/null 2>&1 ; then
+    defaultserverargs="$defaultserverargs -dpi `/usr/bin/defaults read $X11_PREFS_DOMAIN dpi`"
+fi
+
+# Automatically determine an unused $DISPLAY
+d=0
+while true ; do
+    [ -e /tmp/.X$d-lock ] || break
+    d=$(($d + 1))
+done
+defaultdisplay=":$d"
+unset d
+
+whoseargs="client"
+while [ x"$1" != x ]; do
+    case "$1" in
+    # '' required to prevent cpp from treating "/*" as a C comment.
+    /''*|\./''*)
+    if [ "$whoseargs" = "client" ]; then
+        if [ x"$client" = x ] && [ x"$clientargs" = x ]; then
+        client="$1"
+        else
+        clientargs="$clientargs $1"
+        fi
+    else
+        if [ x"$server" = x ] && [ x"$serverargs" = x ]; then
+        server="$1"
+        else
+        serverargs="$serverargs $1"
+        fi
+    fi
+    ;;
+    --)
+    whoseargs="server"
+    ;;
+    *)
+    if [ "$whoseargs" = "client" ]; then
+        clientargs="$clientargs $1"
+    else
+        # display must be the FIRST server argument
+        if [ x"$serverargs" = x ] && \
+         expr "$1" : ':[0-9][0-9]*$' > /dev/null 2>&1; then
+        display="$1"
+        else
+        serverargs="$serverargs $1"
+        fi
+    fi
+    ;;
+    esac
+    shift
+done
+
+# process client arguments
+if [ x"$client" = x ]; then
+    client=$defaultclient
+
+    # For compatibility reasons, only use startxrc if there were no client command line arguments
+    if [ x"$clientargs" = x ]; then
+        if [ -f "$userclientrc" ]; then
+            client=$userclientrc
+        elif [ -f "$sysclientrc" ]; then
+            client=$sysclientrc
+        fi
+    fi
+fi
+
+# if no client arguments, use defaults
+if [ x"$clientargs" = x ]; then
+    clientargs=$defaultclientargs
+fi
+
+# process server arguments
+if [ x"$server" = x ]; then
+    server=$defaultserver
+
+    # For compatibility reasons, only use xserverrc if there were no server command line arguments
+    if [ x"$serverargs" = x -a x"$display" = x ]; then
+      if [ -f "$userserverrc" ]; then
+          server=$userserverrc
+      elif [ -f "$sysserverrc" ]; then
+          server=$sysserverrc
+      fi
+    fi
+fi
+
+# if no server arguments, use defaults
+if [ x"$serverargs" = x ]; then
+    serverargs=$defaultserverargs
+fi
+
+# if no display, use default
+if [ x"$display" = x ]; then
+    display=$defaultdisplay
+fi
+
+if [ x"$enable_xauth" = x1 ] ; then
+    if [ x"$XAUTHORITY" = x ]; then
+        XAUTHORITY=$HOME/.Xauthority
+        export XAUTHORITY
+    fi
+
+    removelist=
+
+    # set up default Xauth info for this machine
+    hostname=`/bin/hostname`
+
+    authdisplay=${display:-:0}
+
+    mcookie=`/usr/bin/openssl rand -hex 16`
+
+    if test x"$mcookie" = x; then
+        echo "Couldn't create cookie"
+        exit 1
+    fi
+    dummy=0
+
+    # create a file with auth information for the server. ':0' is a dummy.
+    xserverauthfile=$HOME/.serverauth.$$
+    trap "rm -f '$xserverauthfile'" HUP INT QUIT ILL TRAP KILL BUS TERM
+    @XAUTH@ -q -f "$xserverauthfile" << EOF
+add :$dummy . $mcookie
+EOF
+
+    xserverauthfilequoted=$(echo ${xserverauthfile} | sed "s/'/'\\\\''/g")
+    serverargs=${serverargs}" -auth '"${xserverauthfilequoted}"'"
+
+    # now add the same credentials to the client authority file
+    # if '$displayname' already exists do not overwrite it as another
+    # server man need it. Add them to the '$xserverauthfile' instead.
+    for displayname in $authdisplay $hostname$authdisplay; do
+        authcookie=`@XAUTH@ list "$displayname" \
+        | sed -n "s/.*$displayname[[:space:]*].*[[:space:]*]//p"` 2>/dev/null;
+        if [ "z${authcookie}" = "z" ] ; then
+            @XAUTH@ -q << EOF
+add $displayname . $mcookie
+EOF
+        removelist="$displayname $removelist"
+        else
+            dummy=$(($dummy+1));
+            @XAUTH@ -q -f "$xserverauthfile" << EOF
+add :$dummy . $authcookie
+EOF
+        fi
+    done
+fi
+
+eval @XINIT@ \"$client\" $clientargs -- \"$server\" $display $serverargs "-xkbdir" "@XKEYBOARD_CONFIG@"
+retval=$?
+
+if [ x"$enable_xauth" = x1 ] ; then
+    if [ x"$removelist" != x ]; then
+        @XAUTH@ remove $removelist
+    fi
+    if [ x"$xserverauthfile" != x ]; then
+        rm -f "$xserverauthfile"
+    fi
+fi
+
+exit $retval
diff --git a/pkgs/servers/x11/xquartz/system-fonts.nix b/pkgs/servers/x11/xquartz/system-fonts.nix
new file mode 100644
index 00000000000..cf24bb439b9
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/system-fonts.nix
@@ -0,0 +1,36 @@
+{ stdenv, xorg, fontDirs }:
+
+stdenv.mkDerivation {
+  name = "xquartz-system-fonts";
+  buildInputs = [
+    xorg.mkfontdir xorg.mkfontscale
+  ];
+  buildCommand = ''
+    source $stdenv/setup
+
+    for i in ${toString fontDirs} ; do
+      if [ -d $i/ ]; then
+        list="$list $i";
+      fi;
+    done
+    list=$(find $list -name fonts.dir -o -name '*.ttf' -o -name '*.otf');
+    fontDirs=''';
+    for i in $list ; do
+      fontDirs="$fontDirs $(dirname $i)";
+    done;
+    mkdir -p $out/share/X11-fonts/;
+    find $fontDirs -type f -o -type l | while read i; do
+      j="''${i##*/}"
+      if ! test -e "$out/share/X11-fonts/''${j}"; then
+        ln -s "$i" "$out/share/X11-fonts/''${j}";
+      fi;
+    done;
+    cd $out/share/X11-fonts/
+    rm fonts.dir
+    rm fonts.scale
+    rm fonts.alias
+    mkfontdir
+    mkfontscale
+    cat $( find ${xorg.fontalias}/ -name fonts.alias) >fonts.alias
+  '';
+}
diff --git a/pkgs/servers/x11/xquartz/xinitrc b/pkgs/servers/x11/xquartz/xinitrc
new file mode 100755
index 00000000000..1a0c2ccf1c0
--- /dev/null
+++ b/pkgs/servers/x11/xquartz/xinitrc
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+export PATH=@PATH@:$PATH
+
+userresources=$HOME/.Xresources
+usermodmap=$HOME/.Xmodmap
+
+# Fix ridiculously slow key repeat.
+@XSET@ r rate
+
+# merge in defaults and keymaps
+
+if [ -f "$userresources" ]; then
+    if [ -x /usr/bin/cpp ] ; then
+        @XRDB@ -merge "$userresources"
+    else
+        @XRDB@ -nocpp -merge "$userresources"
+    fi
+fi
+
+if [ -f "$usermodmap" ]; then
+    @XMODMAP@ "$usermodmap"
+fi
+
+fontpath="@SYSTEM_FONTS@"
+[ -e "$HOME"/.fonts/fonts.dir ] && fontpath="$fontpath,$HOME/.fonts"
+[ -e "$HOME"/Library/Fonts/fonts.dir ] && fontpath="$fontpath,$HOME/Library/Fonts"
+[ -e /Library/Fonts/fonts.dir ] && fontpath="$fontpath,/Library/Fonts"
+[ -e /System/Library/Fonts/fonts.dir ] && fontpath="$fontpath,/System/Library/Fonts"
+@XSET@ fp= "$fontpath"
+unset fontpath
+
+if [ -d "${HOME}/.xinitrc.d" ] ; then
+    for f in "${HOME}"/.xinitrc.d/*.sh ; do
+        [ -x "$f" ] && . "$f"
+    done
+    unset f
+fi
+
+exec @QUARTZ_WM@