Fix ncurses in OS X 10.6.3

In response to my blog post about the issue affecting arrows under OS X 10.6.3, Jonathan Groll pointed out that copying the ncurses libraries from an old 10.6.2 install would fix the problems. It works, but manipulating libraries in that way still feels a bit wrong to me.

And that’s without even mentioning the security implications of getting these libraries from “somewhere on the internet” if you don’t have them laying around anymore.

Instead, let’s fix an Open Source system in the way Open Source is supposed to work: grab the sources and recompile!

Grabbing the sources

Apple provides the sources for the insides of OS X on opensource.apple.com. Considering ncurses-31 (the 10.6.3 version) is buggy, we’ll download a tarball of ncurses-27 (the 10.6.2 version) from Apple right there: ncurses-27.tar.gz.

Recompiling

Step 1: compile.

mbp:ncurses-27 florent$ make

Step 2: install.

mbp:ncurses-27 florent$ sudo make install
Password:
TargetConfig: MacOSX
cd /tmp/ncurses/Build && CFLAGS="-arch x86_64 -arch i386 -arch ppc -g -Os -pipe -isysroot /" CCFLAGS="-arch x86_64 -arch i386 -arch ppc -g -Os -pipe " CXXFLAGS="-arch x86_64 -arch i386 -arch ppc -g -Os -pipe " LDFLAGS="-arch x86_64 -arch i386 -arch ppc            "  /tmp/ncurses/Sources/ncurses/configure \
		--prefix=/usr --disable-dependency-tracking --disable-mixed-case \
		--with-shared --without-normal --without-debug --enable-termcap --enable-widec --with-abi-version=5.4 --without-cxx-binding --without-cxx --mandir=/usr/share/man
/bin/sh: line 0: cd: /tmp/ncurses/Build: No such file or directory
make: *** [install] Error 1

Oookay, by default this Makefile tries to install ncurses into /tmp/ncurses and complains because the directory doesn’t exist. Why not. There has to be an option to change that when calling make, but I went for the fastest choice:

mbp:ncurses-27 florent$ mkdir /tmp/ncurses/Build
mbp:ncurses-27 florent$ sudo make install

And wait for the magic to happen. It’s quite long (and verbose) actually, don’t worry if it takes a few minutes.

Once this was finished, I launched sudo update_dyld_shared_cache and ran some tests: no luck, ncurses still behaved badly.

Then I noticed the “make install” output seemed to show ncurses-27 was actually installed (great!)… In /tmp/ncurses (not so great).

Making it (actually) work

I don’t fully understand how OS X deals with these source tarballs. However, I noticed that /tmp/ncurses/Build contains a Makefile. It looks like what’s installed in /tmp/ncurses is merely an intermediary build that you can then install:

mbp:ncurses-27 florent$ cd /tmp/ncurses/Build/
mbp:Build florent$ make
mbp:Build florent$ sudo make install

I then ran sudo update_dyld_shared_cache again. This time, it prompted:

update_dyld_shared_cache[62767] current i386 cache file invalid because /usr/lib/libncurses.5.4.dylib has changed
update_dyld_shared_cache[62767] current x86_64 cache file invalid because /usr/lib/libncurses.5.4.dylib has changed

Which is good, because it shows we actually changed the compiled version of ncurses (libncurses.5.4)

I finally ran the tests again: ncurses now works like a charm!

OS X 10.6.3 broke ncurses

As I was working on my Seitunes project, I noticed something strange: the arrows didn’t quite work any more. Instead of their proper action (up & down to change volume, right & left to change song), they all quit the program and printed -respectively- “OA”, “OB”, “OC” and “OD” on the stdout.

I tried to go back to a working state by progressively deleting new features I was implementing, until I had exactly the same code as the (known working!) 0.5 version, but it was still quitting. gdb told me it wasn’t a crash (“Program exited normally”).

After some testing, I noticed Seitunes worked on my laptop, but not on my MacBook Pro. The only difference between them being that my laptop was still in OS X 10.6.2, while my mbp has upgraded to 10.6.3.

After a bit of digging into curses functions, I started to suspect keypad(WINDOW *, BOOL) to not work properly after the update. keypad() is supposed to dictate whether getch() should interpret an arrow as one non-ASCII value (with the boolean argument set to TRUE) or a set of ASCII values beginning by the escape char, a.k.a. 27 (FALSE). I explicitly call keypad(stdscr, TRUE) in Seitunes, but the FALSE state would perfectly explain the quit-then-print-two-chars behaviour I had was having: I use the escape character to quit Seitunes.

I wrote two very simple pieces of code -one for keypad true, one for keypad false- that plainly outputs the value returned by getch(). They look like:

#include <curses.h>

int main( int argc, char** argv )
{
	int key;
	initscr();
	cbreak();
	noecho();
	nonl();
	intrflush(stdscr, FALSE);
	keypad(stdscr, TRUE);
	printw("getch() should produce only one value when hitting an arrow.\n");
	while ( (key = getch() ) != 'q' ) {
		printw("You entered key %d\n", key);	
	}
	endwin();
	return 0;
}

Code and makefile available here (testCurses.zip) if you want to give it a try.

Under both OS X 10.6.2 and Linux Mint 6 “Felicia” (based on Ubuntu 8.10), these programs behave as they’re supposed to: when keypad is TRUE, an arrow is shown as a single value; when FALSE, an arrow becomes a set of values.

Under OS X 10.6.3, these two programs behave the same way. Both output several values for an arrow.

I filed a bug report to Apple (vintage interface by the way!).

While this bug is present, we’ll have to manually parse the ASCII values for the arrows, which are mapped as follows:

Up	27 79 65	('escape' 'O' 'A')
Down	27 79 66	('escape' 'O' 'B')
Right	27 79 67	('escape' 'O' 'C')
Left	27 79 68	('escape' 'O' 'D')

Edit: these values assume OS X 10.6.3 and keypad(stdscr, TRUE), a.k.a. when the bug is present.

If you want to use keypad(stdscr, FALSE) in 10.6.3, the arrows are mapped as:

Up	27 91 65	('escape' '[' 'A')
Down	27 91 66	('escape' '[' 'B')
Right	27 91 67	('escape' '[' 'C')
Left	27 91 68	('escape' '[' 'D')

Update, March 1st: Apple answered to my bugreport (ID #7812788). They told me it was a known issue (duplicate of bug #7812932) currently being investigated by engineering.

Seitunes, an iTunes command-line interface

My home main computer is a MacBook Pro, on which I frequently play music with iTunes. However, I’m often on my laptop, without direct access to the MBP’s screen or keyboard/mouse to pause, change song, change volume, etc. I can connect to the MBP using VNC, but I was looking for something more lightweight.

I therefore decided to design a command-line interface for iTunes, that I would run via SSH. I called it Seitunes for reasons I can’t really remember right now, but there it is!

Seitunes is

– written in C and interfaces with iTunes through AppleScript
– designed for OS X – should be compatible with quite old versions actually, because it doesn’t rely on a lot of cutting edge features
– built upon the curses library
– very very small
– still under development
– Free software (GPLv3)
– available here

Features

> Display iTunes playing track and status

Seitunes main screen

> Control iTunes playback (play/pause, volume, next song/previous song)

Seitunes, main screen, playing, with help

> If iTunes is stopped when Seitunes starts, it starts iTunes and starts a song from the Library.

To do

> Add more tests to better check iTunes state and not trigger Applescript errors
> Add info about playlists in order to be able to play a specific playlist instead of the whole library
> Add an option to toggle shuffle
> Implement the “quit iTunes” function and check that it doesn’t cause more Applescript problems

Known bugs

> An error message flickers when an Applescript error is triggered (often when iTunes quit while Seitunes is opened)