This note describes setting up a development environment for doing X Windows Development on Mac OS from the ground up. The notes do apply to other environments… with minor tweaks (I tried the same basic setup on Debian Linux, DragonFly BSD, and FreeBSD with no major issues).

This is enough setup to build applications in X Windows using Xlib - the lowest level of programming in X… other than the X Protocol :). I did this because I have developed an interest in how graphical interfaces work and X, for all its quirks, is not self-limiting, crippled, partially proprietary or any of that nonsense and it is widely used.

After setting up, downloading some source, building and deploying, here’s what we’re looking at… on a Mac:



System Requirements

  • MacOS - I’m running this on Mojave, but have tested it on Monterrey as well
  • XQuartz - I’m running 2.8.4
  • Macports - I’m running 2.8.0
  • Some code to run in X - I’m using xeyes from X itself and basicwin from Volume One of The Definitive Guides to the X Window System: Xlib Programming Manual for Version 11 R4/R5 by Adrian Nye

Getting MacOS Ready

There are only a few small steps to take to prepare MacOS for doing X Windows development:

  1. Install XQuartz
  2. Install MacPorts (to satisfy any dependencies and fill tool gaps like imake)
  3. Install imake
  4. Configure and Test XQuartz
  5. Build xeyes and basicwin
  6. Celebrate

1. Install XQuartz

Head over to and download the install package. Run it. Logout as advised when install is complete and log back in.

2. Install MacPorts

Browse to and grab an installer for your version of MacOS. Run it.

3. Install imake

Open terminal and type sudo port install imake to install it.

4. Configure and test XQuartz

Start XQuartz (it’s in /Applications/Utilities)

  • Open Preferences
  • Click Input
  • If you need 3 button mouse emulation (trackpad), Check the box
  • Check Enable key equivalents under X11
  • Click Output
  • Check Full-screen mode and Auto-show menu bar in full-screen mode
  • Click Pasteboard and enable any helpful settings you like

Note: When you enable full-screen mode, your x windows will appear on a separate desktop. This is different behavior than you will be used to if you have already been using X on your Mac, but it’s more in line with a typical x windows environment this way

Investigating the XQuartz startup file situation

I wanted to use twm as the window manager instead of quartz-wm. So, I made some additional changes to the startup environment.

Finding xinitrc

On Mac OS, XQuartz provides the X11 environment (the server and many of the clients). The server is configured via xinitrc. This file can be found using find:

find /opt -name xinitrc
Looking at the delivered xinitrc

A look at this file shows that it provides a mechanism for customization using an .xinitrc.d directory:

cat /opt/X11/etc/X11/xinit/xinitrc


# merge in defaults and keymaps

if [ -f $sysresources ]; then

    if [ -x /usr/bin/cpp ] ; then
        xrdb -merge $sysresources
        xrdb -nocpp -merge $sysresources


if [ -f $sysmodmap ]; then
    xmodmap $sysmodmap

if [ -f "$userresources" ]; then

    if [ -x /usr/bin/cpp ] ; then
        xrdb -merge "$userresources"
        xrdb -nocpp -merge "$userresources"


if [ -f "$usermodmap" ]; then
    xmodmap "$usermodmap"

# start some nice programs

if [ -d /opt/X11/etc/X11/xinit/xinitrc.d ] ; then
	for f in /opt/X11/etc/X11/xinit/xinitrc.d/?*.sh ; do
		[ -x "$f" ] && . "$f"
	unset f

twm &
xclock -geometry 50x50-1+1 &
xterm -geometry 80x50+494+51 &
xterm -geometry 80x20+494-0 &
exec xterm -geometry 80x66+0+0 -name login

I have included the entire file because it is worth seeing, but the relevant portion is at the end of the file:

if [ -d /opt/X11/etc/X11/xinit/xinitrc.d ] ; then
	for f in /opt/X11/etc/X11/xinit/xinitrc.d/?*.sh ; do
		[ -x "$f" ] && . "$f"
	unset f

This snippet looks in /opt/X11/etc/X11/xinit/xinitrc.d for any .sh files and runs them.

Looking in the /opt/X11/etc/X11/xinit/xinitrc.d directory

To see what’s going on, take a look at the directory:

ls /opt/X11/etc/X11/xinit/xinitrc.d

They’re each interesting in their own right, but the one we’re interested in is Let’s take a look at it:

cat /opt/X11/etc/X11/xinit/xinitrc.d/
if [ -d "${HOME}/.xinitrc.d" ] ; then
        for f in "${HOME}"/.xinitrc.d/*.sh ; do
                [ -x "$f" ] && . "$f"
        unset f

Well, that looks familiar. It appears that it looks for a .xinitrc.d folder in the users directory and runs any .sh files it finds. That’s our hook for running twm.

Setting twm as the window manager

To set twm as the window manager requires us to create a startup file and give it execute permission:

mkdir -p ~/.xinitrc.d
cat <<EOF > ~/.xinitrc.d/
exec twm
chmod +x ~/.xinitrc.d/

Test twm

Close XQuartz if it is currently running so that it will pick up the changes you have made up to this point and reopen it. To change to the desktop, ensure that XQuartz has the focus (click on it) and type the three key combination Command-Option-a or from the menu, select XQuartz->Toggle Full Screen:


Your screen should switch to twm. Position your mouse somewhere near top left to position the xterm window:


To switch between your x windows session and MacOS, just press Command-Option-a or move your mouse to the top of the screen and from the menu, select XQuartz->Toggle Full Screen.

Your X Windows will continue running until you either exit twm, or close XQuartz. The proper way to do it is to close any running application and exit twm. In order to close open applications, you can type CTL-d to close the applications input (works for terminal and a lot of unix utilities) or use the kill command from the twm menu. To bring up the twm menu, left click on the desktop, outside of any window, while in X Windows, and the twm menu will appear:


To exit twm, bring its menu up and click exit.

Note: Occasionally, in my experience, if you don’t exit twm cleanly, it will display a phantom window frame on the screen when you start it up (no big whoop, but annoying). So, exit correctly or ignore the cruft.

Read more about twm at

From now on, we will be running any x applications in twm. Just note that they will also work fine in xquartz-wm and appear to be more integrated with MacOS, then. We can build our apps in a regular MacOS terminal and enjoy easy cut and paste, but let’s run the apps in xterm.

5. Build xeyes and basicwin

Now that we have a working X Window environment to explore let’s try building a couple of apps. First, let’s build my all time favorite X client, xeyes.

Get the xeyes source

cd ~/sandboxes-git
git clone
cd xeyes

Build xeyes from source

I won’t apologize for this next bit, suffice it to say that some sorcery is involved… and glomming the XQuartz Developer Information page at The instructions there are way out of date, but they do give a hint as to how to set up your environment for building X applications from the X.Org distribution (of which xeyes is part).

That said, I will apologize, in advance, if your build fails due to missing dependencies (like autoconf). I have been running this system for a while and I do a lot of development on it, so I probably have already installed some of the dependencies (how would I know, right?). Anyway, if you do hit a notice that you are missing a library or dev tool, just sudo port install it.

Now, without further ado, let’s build xeyes:

export ACLOCAL="aclocal -I /opt/X11/share/aclocal"
export PKG_CONFIG_PATH="/opt/X11/share/pkgconfig:/opt/X11/lib/pkgconfig"
export CFLAGS="-Wall -O0 -ggdb3 -arch x86_64 -pipe"
autoreconf -fvi
./configure --prefix=/opt/X11 --disable-dependency-tracking

These commands tell autoreconf, configure, and make, respectively where to find pkgconfig, aclocal, etc., and then execute the command to build the app.

If all is well in the world, you should see:

Making all in man
  GEN      xeyes.1
  CC       Eyes.o
  CC       transform.o
  CC       xeyes.o
  CCLD     xeyes

and, the file xeyes should now be present in the directory:

ls -l xeyes
-rwxr-xr-x  1 wsenn  staff  30944 Jan 24 18:34 xeyes

Yay, let’s run it. Switch over to your twm and open an xterm. In the xterm window, run xeyes:

cd ~/sandboxes-git/xeyes


Get the basicwin source

OK. That was fun, but we need to get closer to our goal of doing Xlib development. Let’s build basicwin from the Xlib Programmer’s Manual.

mkdir -p ~/sandboxes-git
cd ~/sandboxes-git
git clone xlib-pm

Create a temporary location for our source code

mkdir -p ~/work
cd ~/work
tar xvf ~/sandboxes-git/xlib-pm/xlibprgs4.tar.Z
cd xlib/basicwin/basic

Build basicwin from source

Unlike xeyes, above, we don’t need any special incantations to make this next part work. We can follow what Adrian Nye wrote nearly 30 years ago:


If all went well, we will see something along the lines of:

19 warnings generated.
rm -f basicwin
/usr/bin/cc -o basicwin -Os -Wall -Wpointer-arith -no-cpp-precomp     -L/opt/local/lib   basicwin.o -lXext -lX11      

Nevermind the warnings, a lot has changed with our c environment over the years. The good news is we now have a new executable:

ls -l basicwin
-rwxr-xr-x  1 wsenn  staff  14792 Jan 24 18:48 basicwin

Switch over to twm, open a new xterm, and run it:

cd ~/work/xlib/basicwin/basic


6. Celebrate

Yeeha! That’s worth celebrating. Next, tweak that basicwin source code. get a book on Xlib and really dig in, get one on XT, get one on Motif, go crazy and read up on QT and GTK+, just have fun. I’m just getting started, so it’s all new to me. I’m planning = to start with Xlib and work my way up the stack.

Some extras

The more observant folks prolly wondered a few things:

  1. Where did that browser come from in the cool screenshot?
    sudo port install midori

    Midori rocks! Super fast and runs in X. I use it over ssh all of the time - firefox is great, but it sucks over the network. Not so midori!

  2. Same question for the other app?
    sudo port install htop

    htop is a pretty process monitor for terminal (and xterm).

  3. How did he capture those screenshots. This took quite a bit of trial and error. Folks on the web recommend using xwd, I couldn’t get it to grab the right window (the desktop) no matter what incantation I gave it. What did work was Mac OS’s built in application. I just gave it a 10 second timeout, chose the select window option, clicked Capture, and then speedily opened twm and waited for the timeout to occur.

Links to high res images:

post last updated 2023-02-13 21:02:00 -0600