Amateur Topologist

Everything but topology.

Tag: os x

Handling modifier keypresses in Cocoa

One of the features of Snow Leopard is the addGlobalMonitorForEventsMatchingMask:handler: method, which lets you handle events in other programs. This requires the user to have enabled accessibility in System Preferences, or for the program to be running as root and grant itself trusted status, but it lets you install a handler that will be called whenever any event that matches the relevant mask occurs outside of your application (for events inside your application, use addLocalMonitorForEventsMatchingMask:handler:. One of the things that you can capture is the pressing of a modifier key, such as Cmd or Option without a corresponding ‘real’ key. If your mask includes NSFlagsChanged, then any press or release of a modifier key will send your handler an NSEvent that you can call modifierFlags on to get an NSUInteger corresponding to which modifier keys were actually pressed. But there’s a problem here: if you hold down Command and repeatedly press the Shift key, your handler will be called for both the keypress and the key release. So if you’re counting the number of presses of each modifier, you’ll count the Command key again each time you press or release the Shift key, which is probably not what you want!

The solution is to use a block. A block is like a closure in other languages; we can use it to create a function that tracks a bit of state around with it. In this case, we want our flag handler function to track what modifier flags were present the last time it was called; if we compare the set of flags in the new invocation to those from the old invocation, we can determine what changed, and therefore what actually happened:

__block NSUInteger lastFlags = 0;
void (^flagTrigger)(NSEvent*) = Block_copy(^ (NSEvent *event) {
    NSUInteger flags = [event modifierFlags];
    NSUInteger masked = flags & ~lastFlags;
    lastFlags = flags;
    // flags now only includes keys that were actually pressed
    // handle those keys here
};

[NSEvent addGlobalMonitorForEventsMatchingMask:(NSFlagsChangedMask) handler:flagTrigger];

Here, we declare a lastFlags NSUInteger that we’ll use to store the previous set of pressed modifier flags; by declaring it using __block, we note that the block is allowed to modify it. We then create a block flagTrigger, whose type states that it takes an NSEvent* and returns void, and which handles the logic: when it’s called, it gets the modifiers in the event, and applies a mask consisting of just the flags that weren’t already set last time. We then store the flags variable into lastFlags for the next time, and then process the masked flags. Finally, once we’ve declared the block, we install it as the handler.

The code above is basically copied wholesale from a project I’m working on called KeyboardViz, specifically the Keyboard class which displays an onscreen keyboard that changes colors as you press keys. I’m using it to learn ObjC and Cocoa, so suggestions on coding style are welcome.

Making Terminal.app suck less

For various reasons (mostly due to the fact that iTerm would regularly spike in CPU usage whenever I was causing it to redraw large portions of the screen, causing noticeable lag), I’ve been forced to switch away from iTerm as my terminal emulator of choice, back to the standard Terminal.app. It’s a decent piece of software, but it has a few issues of its own.

First, it’s 16-color only, and there’s no way to change the colors. While the former can’t be helped, the latter can: if you install SIMBL, then download TerminalColors (either the Leopard version or the Snow Leopard version, whichever is appropriate), then unzip and move the resulting directory into /Library/Application Support/SIMBL/Plugins, then restart, you’ll be able to modify colors under Settings -> Text by clicking the new ‘More’ button.

Second, the Home and End keys, to put it bluntly, suck. They don’t work in most programs. The fix for this is to go to Settings -> Keyboard, then creating/editing the entry for the home key. Select ‘send string to shell’, and in the text entry field, press escape, then capital O, then capital H, so it reads \033OH. Then do the same for the end key, only this time insert \033OF. It should work pretty much everywhere. Page up is \033[5~  and page down is \033[6~.

There are a couple other things: add
alias grep="grep --color=auto"
alias rgrep="grep -r --color=auto"
alias grip="grep -i --color=auto"
alias rgrip="grep -ir --color=auto"
alias ls="ls -G"

to colorize ls and grep by default, as well as add some convenient aliases (grip for case-insensitivity, rgrep for recursive grep).