A Seamless X11 Experience on OS X
By Calum MacRae
May 4, 2015
Introduction
When Youri and I first set out hacking on OS X, before the real inception of Save OS X, we spent quite some time chasing an X11 set up that felt as if we were on any other *nix platform that we had become accustomed to. We never did quite get it exactly the same. I even tried, at one point, taking apart and hacking about with some of the sources from the PureDarwin project. At the time, I felt I was very close to getting X11 to run natively… though, in hindsight, I think that feeling was more down to my naivety.
That said, we did reach a set up that we both became quite happy with and used extensively - that's what I'll be talking about in this post.
Why would I want to run X11 on OS X?
It's funny, it seems quite a sought after "achievement" to run X11 on OS X in a manner close to the BSDs/Linux/other *nixes. I quote achievement here, because, well, I don't think it really warrants the use of that word - but it seems that's what the community deems it as when people reach it.
Nowadays, I don't touch X11 on OS X, but I can understand the appeal. If you become proficient in a window manager of choice, or other tools that are only available in an X11 environment, it's perfectly rational to want to continue doing so on OS X. When I was using X11 on OS X heavily, I did everything in it (with the exception of web browsing). It fit quite nicely, I'd have one workspace that housed an X11 environment, and another which simply had my browser.
Other than wanting to reside within a familiar environment, I think it's safe to say there's a particular motive from some individuals to run X11 on OS X because it just seems cool… I'll leave it at "cool" - it's fairly ambiguous, but it's good enough. Wanting to do something because it's "cool", often when relating to hacking something (in a curious sense, not malicious), is a pretty fair reason and can often help spur the individual on to learn more and discover more useful/practical projects.
Why I'm writing about this
With some changes I made earlier today to the SaveOS X bootstrapper, removing the clunky collection of scripts & plists, I deprecated support for our X11 hacks. Save OS X has drifted from our initial goals and now focuses primarily on an easy to set up implementation of classic binary package management on OS X using pkgsrc.
It actually started off as a little script that would go through and strip out a bunch of "crap" that Apple had included in OS X, with the intention of saving disk space and other resources.
But, I digress; I figured I'd preserve the "achievement" in this write-up, for anyone else out there who wants to implement the same.
Some eye candy
Here's how my MacBook Air looked in January 2014. You can see bspwm running on OS X here. This was the culmination of a few little tricks - this isn't even running in 'Full-screen' mode.
The software/hacks
- XQuartz
- Installation of X11 software (wms, utils, libs, etc)
- Property List trickery
- Custom
launchd
service
XQuartz
Up until Mountain Lion, Apple used to include a native X11.app in OS X, which - as you may have guessed - allowed users to run their X11 applications. With its removal, users turned to XQuartz to fill the gap.
I don't feel I need to touch on this much. You can simply go to the official XQuartz site, download, and install as you would most other software on OS X.
Installation of X11 software (wms, utils, libs, etc)
Let's say you're on a BSD or Linux distro: you want to install a new window manager or desktop environment. You'd usually just do that through the package manager, right? Well, same approach with OS X really, unless you're more comfortable building from source.
So, of course, I'm going to recommend Save OS X, but you could use any other package/ports managers available to get your actual X11 applications.
When using
Save OS X this would be as simple as something like sudo pkgin -y in twm
,
then setting up your ~/.xinitrc
as you would on any other system:
echo "exec twm" > ~/.xinitrc
, for example.
Property List trickery
This next one depends how you'd like to interact with X11 and the extent of functionality that you desire.
When using XQuartz, you can opt to use a setting called 'Full-screen mode', found in XQuartz's preferences, under the 'Output' tab. This will, as one might be able to surmise, make XQuartz full screen. That is to say, the X11 server will occupy your entire display. Use of this option is as close to a 'standard' X11 set up as you can get. It allows you to use root-menus, Aqua's keybindings don't interfere, and you don't see any Aqua interface.
Whilst the above is nice, you may want to occupy your desktop with a mix
of X11 & Aqua windows simultaneously. In doing so, you may be such a
perfectionist that you don't want to see the dock or menubar - that was
the case for me at least. You can do some trickery with Aqua
applications' Info.plist
to make certain UI elements behave a
particular way.
To hide the dock and menubar when using an Aqua application, you can add
the following properties to its Info.plist
(usually located at
/Applications/AppName.app/Contents/Info.plist
):
<key>LSUIPresentationMode</key> <integer>4</integer>
This won't always work and its placement can vary. Simply replacing
XQuart'z Info.plist
with this one should achieve the desired behavior.
Custom launchd
service
If you're heavily reliant on X11, and plan to use it a lot, you can have
it auto-start using a launchd
service.
Place something like the following in
~/Library/LaunchAgents/org.saveosx.startx.plist
<?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>EnvironmentVariables</key> <dict> <key>LANG</key> <string>en_US.UTF-8</string> <key>LC_ALL</key> <string>en_US.UTF-8</string> <key>PATH</key> <string>/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/pkg/bin:/usr/pkg/sbin</string> </dict> <key>Label</key> <string>org.saveosx.startx</string> <key>Program</key> <string>/opt/X11/bin/startx</string> <key>RunAtLoad</key> <true/> <key>StandardErrorPath</key> <string>/var/log/saveosx.log</string> <key>StandardOutPath</key> <string>/var/log/saveosx.log</string> </dict> </plist>
It should be quite clear from the above what's happening here. It's just like any other service management manifest, but uses XML to define keys and their values - just like SMF.
Once the above is in place, you can import the service and have it set
to launch upon login by issuing the following command
launchctl load -w ~/Library/LaunchAgents/org.saveosx.startx.plist
Some small caveats/tips
Well, of course it's not perfect…
XQuartz focus
One problem with going the non-Full-screen path is that getting XQuartz
to focus upon first launch can be quite a pain. I tackled this by simply
having an instance of urxvt
launch along with my window manager. If
you use a panel/bar, this isn't as much of an issue, since your WM/DE
has already launched an X11 tool which you can simply click on to gain
X11 focus. However, if you're using a lightweight window manager,
without any panel/bar - as there is the absence of a root-window
(selecting the desktop will just focus Finder) - it can be quite
awkward. Simply having a terminal, or something else in X11, launch
automatically circumvents this issue.
Of course, if you opt to use 'Full-screen', you needn't worry about this.
Opening Aqua applications from XQuartz
Say you want to launch an Aqua application from dmenu
, or you need to
send a URL to Safari from urxvt
. This is where the open(1)
utility
comes in handy. If you wanted to be able to open Safari using dmenu
you could simply add a little shell script somewhere in your PATH
containing the following:
#! /bin/bash open -a Safari
Or if you're using the urxvt-perls
perl extensions for urxvt
and you
need to tell it what to launch URLs with:
Urxvt-url-select.launcher: open -a safari
Which reminds me, the following can be used with urxvt-perls
for
clipboard management:
URxvt*perl-lib: /usr/pkg/lib/urxvt/perl/ URxvt*perl-ext-common: default,clipboard URxvt*keysym.M-c: perl📋copy URxvt*keysym.M-v: perl📋paste URxvt*clipboard.copycmd: pbcopy URxvt*clipboard.pastecmd: pbpaste
This is all dead simple, but makes it a much more enjoyable experience. I didn't delve too far into integrating the two "sides", but I'm sure you could do some pretty nifty stuff with Automater/AppleScript.
Too lazy? Use the legacy script!
If you've got Git installed, you could simply clone the bootstrap repo and checkout a point in history that still implemented all the above functionality for you:
$ git clone git@github.com:cmacrae/saveosx $ cd saveosx $ git checkout 1c79fa66d99ae5a841b763ab5a29841b7458dba5 $ cd scripts $ ./bootstrap