Visor + Nocturne, the reverse SEP field
Every now and then I am presented by an irresistible challenge, and this is usually because it is ‘SEP’ (Someone Else’s Problem) which is invariably easier and more attractive than the problem that I myself have to solve.
I’m a newbie when it comes to hacking on OSX plugins and whatnot, so this is the tale of a newb survival. Read on if you dare.
The setup
My coworker uses two great pieces of software called Visor and Nocturne, both created by Alcor before he went to work for Google.
Nocturne is a great tool that will invert the screen colors to allow you to work at night without burning your retinas or waking your spouse via the bright screen next to them in bed.
Visor is a cool little SIMBL plugin that turns your Terminal.app into a Quake-style console.
The problem
Normally my coworker uses a black background with white text in his Visor terminal windows. But when using Nocturne this becomes the retina destroying white background with black text.
Step One: grab source code
Thankfully the source code for Nocturne and Visor are both available.
Nocturne, it turns out, uses an undocumented CoreGraphics call to do the screen inversion. This call, CGDisplaySetInvertedPolarity(flag) applies itself to a whole display at a time.
Visor, for its part, applies its own color profile to new terminal windows.
Step Two: formulate possible solutions
A couple of possible solutions immediately jump out.
- Implement a hot-key in Visor that will change the color profile to something that is white background with black text (such that Nocturne will invert this back to the white on black desired)
- Implement a hot-key in Visor that will invert just the Visor terminal’s colors using a CoreImage filter
The thing about an SEP is that the solution that I pick needs to be simple enough to do quickly (after all, it isn’t my problem) but difficult enough that I actually learn something.
So for arbitrary reasons I chose to implement solution #2.
Step 3: implementation
So it turns out that the day of this SEP I saw a post on twitter by @andy_matuschak about a SIMBL plugin he created called Grayifier that turns all non-active windows into a grayscale version of themselves.
The core goodness of this plugin is that it uses another undocumented CoreGraphics call to apply a CoreImage filter to just one window. So the (simplified and condensed) code to apply an inversion filter would look a bit like this:
CGSWindowFilterRef inversionFilter;
CGSConnection connection;
CGSNewConnection(NULL, &connection);
CGSNewCIFilterByName(connection, (CFStringRef)@"CIColorInvert", &inversionFilter);
CGSAddWindowFilter(connection, [window_ windowNumber], inversionFilter, 1 << 2);
To remove the filter, the corresponding call is to CGSRemoveWindowFilter(conn, window, filter).
The last thing that needs to be done is to hook in a hot-key handler. I decided to use Opt-Esc.
EventHotKeyRef invertHotKey = [dispatcher registerHotKey:53
modifiers:NSAlternateKeyMask
target:self
action:@selector(toggleInversion)
whenPressed:YES];
One snag was the fact that the Visor project contained a version of CGSPrivate.h that did not include some of the CoreGraphics functions used above. The easy fix was to replace it with the CGSPrivate.h from Grayifier.
Result: success
So, the end result is that with one extra hot-key, Visor can now invert its own window, which makes life with both Visor and Nocturne much happier.
But more importantly, I found out a bit more about SIMBL plug-ins, some private CoreGraphics calls, and how some cool OSX utilities work.