Topics > Blog
Blog

The blog, managed by jediknil himself.

Recently I had occasion to deal with the Finder, beyond what NSFileManager or NSWorkspace could handle. Now, the easy way to do this is through AppleScript, and Cocoa does provide a way to run AppleScripts (NSAppleScript). The trouble is, (a) NSAppleScript is slow and only runs on the main thread, and (b) you can't pass input easily.

AppleScript is built on Apple events, a mid-to-high-level form of interprocess communication that's been around since System 7. Apple events can be constructed and sent off using either pure C code (what's now part of the CoreServices framework) or the Objective-C wrapper, NSAppleEventDescriptor, in the Foundation framework. But constructing a proper Apple event is a little onerous, and again, customizing the message being sent has to be done interspersed with the building of the event. Even with the convenient AEBuildAppleEvent() function, which takes a stringified version of the event, it's just not transparent and simple.

Enter Scripting Bridge. It was announced and trumpeted (at moderate volume) way back before Leopard was released—it is, of course, a 10.5-only feature. And somehow it slipped beneath my radar, though of course most of my apps were/are backwards compatible to 10.4 or even 10.3. The ADC site had a rather trivial example that actually shows the most important part of the bridge: it looks like Objective-C and uses regular Foundation classes whenever possible.

For example, using the Bridge, you could get the name of the current iTunes track with the following line of code:

NSString *currentTrackName = [[iTunes currentTrack] name];

The Scripting Bridge uses native Cocoa data types, such as NSString and NSArray, requires far less code than using an NSAppleEventDescriptor, and runs more than twice as fast as a precompiled NSAppleScript.

The fallout benefit was that all the languages bridged with the Cocoa runtime, like Ruby, Python, and F-script, also get this functionality. It's in that context that I've heard Scripting Bridge mentioned since the Leopard launch. But let's take a look at what it would do to a bit of Dockyard code:

result = AEBuildAppleEvent(kAECoreSuite, kAESetData, typeApplicationBundleID, systemEventsIDChars, systemEventsIDLength,
  kAutoGenerateReturnID, kAnyTransactionID, &event, NULL,
  (([OrientationLeft isEqual:[defaultsDict objectForKey:OrientationKey]]) ?
    "data:left,'----':'obj '{form:prop,want:prop,seld:dplo,from:'obj '{form:prop,want:prop,seld:dpas,from:()}}" :
    [OrientationRight isEqual:[defaultsDict objectForKey:OrientationKey]] ?
      "data:righ,'----':'obj '{form:prop,want:prop,seld:dplo,from:'obj '{form:prop,want:prop,seld:dpas,from:()}}" :		
      "data:bott,'----':'obj '{form:prop,want:prop,seld:dplo,from:'obj '{form:prop,want:prop,seld:dpas,from:()}}"));
if (result == aeBuildSyntaxNoErr)
{
  result = AESendMessage(&event, NULL, kAENoReply, 0);
  AEDisposeDesc(&event);
}

Bleah. It works. It's a lot more efficient than calling out to an AppleScript. But it's certainly not clear what's going on. Compare that to this (though untested):

id orientation = [defaultsDict objectForKey:OrientationKey];
SystemEventsApplication *sysEvents = [SBApplication applicationWithBundleIdentifier:@"com.apple.systemevents"];
if ([OrientationLeft isEqual:orientation]) {
  sysEvents.dockPreferences.location = SystemEventsDplsLeft;

} else if ([OrientationRight isEqual:orientation]) {
  sysEvents.dockPreferences.location = SystemEventsDplsRight;

} else {
  sysEvents.dockPreferences.location = SystemEventsDplsBottom;
}

Which one's easier to read?

So, there is a downside. Scripting Bridge is very simple when it works, but downright impossible to debug when it doesn't. With several layers of indirection and insulation from the Apple event architecture, you can't really do things "manually" with Scripting Bridge. (There is a -propertyWithClass:code: method that lets you override some default behavior, particularly when SB isn't able to determine the right class for you.) The one error-recovery method isn't actually very useful for error-recovery, and it's the one place where the framework unexpectedly drops you down to the C level. I would expect them to at least wrap the event in an NSAppleEventDescriptor, but no...

In the end, I was able to do almost everything I needed to while staying above the bridge. Debugging is hard, but there generally is a way to do everything. (Hint: use URLs to refer to filesystem items, not paths.) But there is another high-level alternative: appscript. Appscript is a very similar framework optimized for Python, Ruby, and then Objective-C. The project states that it isn't yet feature-complete for Objective-C, but it's both stable, being around since Panther, and still under active development. ...But I haven't used it myself.

Now, implementing scriptability is still unfortunately annoying if your app doesn't fit a nice model. Fortunately we've been insulated from raw AppleEvents on that end for a while now. I'll try to document my efforts in that field next time it comes around.


As any programmer knows, feature creep is dangerous, deadlines tend to slip, and work expands not just to fill the time allotted, but far past.

As a Dockyard fan would know (though I doubt there are any), Dockyard 2.0 is now almost two years late. Yes, there was supposed to be a release shortly after Leopard, after I could work out the kinks. What happened?

Well, first I didn't get my hands on Leopard for quite a while. Then I noticed one of its big new features—Spaces—had turned out to be a big new problem for Dockyard. (Basically, the Dock controls Spaces, and Dockyard forces the Dock to reload by quitting it. The result would be windows getting "trapped" in another Space and suddenly popping back...or not.) How to fix this... I wondered. And the current Dockyard alphas do have an imperfect workaround in place now.

But that's just an excuse. The real reasons for Dockyard's inordinate delays are threefold...

Time. As a college student, webmaster for multiple sites, developer supporting a few existing projects, singer and arranger for various groups, etc., I underestimated the amount of time that I could put towards new development of long-term personal projects. The "ten-hour Tetris" a few months back was a sort of self-dare, about what could be accomplished in a short time. But Dockyard isn't a short-term project, it's a large project, which I have high standards for.

A surprising factor was Conflict of Interest. I haven't mentioned it on this site before, but last summer I was fortunate enough to have an internship at Apple, Inc. Yes, the Apple. This summer I'm back there again (though in a different group), and have the same problem: working on independent Cocoa apps (maybe even web apps) can definitely be a conflict of interest. Particularly apps like Dockyard and the Safari plugin I hinted at a few weeks ago, which are full of what's been called "undocumented goodness" but what could easily be construed as "inside information". To keep things clear, I'm not touching Cocoa development during the summer.

(By the way, if you want to tell me about something you want fixed, don't. File a bug (and here too) — we do read them, though a few months before a new OS is released is probably not the best time for a response.)

The biggest problem, though, is what I said before: Dockyard is now a Large Product. When did that happen?

Let's look at Dockyard itself. Dockyard v1.0 was a simple menu listing of docks. You couldn't even change the name of a dock after it had been created, and you couldn't launch items from the menu. The feature that was so valuable to me, after the core functionality, was the implementation as a menu extra, a first-class status item like the clock, battery, or even the Scripts Menu. One of several undocumented strategies in a app (actually a plugin) written during my first year of Objective-C.

Dockyard 1.5 added renaming (hooray) and that obvious-but-great feature of opening items in another dock by choosing them from a submenu. Other dock switchers already did this, but it worked well in Dockyard. No problems yet.

Dockyard 1.6...menu extra, plus widget, plus manager application. Uh-oh! The old Dockyard format was stretching, and a lot of code went into keeping these three in sync if you had them all open. Plus, killing the Dock also means killing the Dashboard. I turned that into a feature by allowing you to switch Dashboard configurations along with your dock. But the current system was untenable.

And now we came to the feature requests and expectations: integration with VirtueDesktops or another pre-Spaces desktop manager, integration with Spaces itself, global hotkeys, switching via screen corners or sides (I had liked this feature in another dock switcher), and of course, not disrupting Spaces to the point of unusability. All of these gave me delusions of grandeur, making me envision a grand plug-in model for switch triggers and switch actions, and an implementation based on distributed objects, instead of keeping separate Dockyard "clients" in sync.

Alas, such visions were much too far-reaching. The one bug that's a show-stopper is the Spaces-disruption, and everything else merely drained my time and made the project spin out of control. I don't want to abandon the project, but I look around at Dock Spaces and Docks and see two products with nice UIs that implement the core functionality as well as I do, or better. (Actually, Dock Spaces used to have better UI than it does now. Oh well.)

Both of those still use the "kill-the-dock-to-reload" strategy, though, so both are subject to Leopard's occasional Spaces-related window-loss. And neither has a true menu extra, though both have the Apple-supported "status item" available. (The difference to the user is mainly that menu extras can be reordered, instead of just adding on to the left end of the status bar. The difference to the developer is that menu extras run inside a single app called SystemUIServer, and can have a few extra features beyond having a plain menu.) It's just like that situation four, five years ago: there are apps that do it, but not the way I'd do it. Only now I have three large roadblocks keeping me from doing it my way.

So, a plan. First, assume Dockyard is not coming back. GenericToolbar has already been retired, and PHP/CC was never updated for Xcode 3. (By the way, Xcode is not easy to reverse-engineer, basic support for other languages got a little better anyway, and I'm not working on PHP-based projects these days. Don't expect an update.) That leaves Webmailer as the lone released project for Belkadan Software right now, and while it deserves a quick look-over, I think it's actually doing pretty well. The Safari keyword plugin will join it hopefully in early autumn.

As for Dockyard, I still want that functionality. Here's a short list of must-have features:

  • Must not break Spaces (unless the user tells it to use old-style switching)
  • Menu extra, with submenus for the contents, i.e. "at least what v1.5 had"
  • Scripting support, so it can be controlled from outside
  • Distributed notifications, so other things can react to it
  • Switch docks, picking up user modifications along the way

And here's some nice-to-have features:

  • Manager interface. Hopefully a better one than the 1.6/1.0 release.
  • Global hot-keys
  • Screen corners and edges

Maybe some day in the future I'll get to all of these. But meanwhile, I'm going to have to retire Dockyard as well. It's had to suffer the embarrassing "10.3 - 10.4" on the front page of Belkadan Software long enough. Come the next site redesign (hopefully before the end of summer, but maybe not until the Safari plugin's release), Dockyard (and GenericToolbar) will be leaving their place of prominence and relegated to a "past products" section.

Apologies to anyone who was desperately waiting for Dockyard 2.0. I'm disappointed, too, but it's far past time to be realistic about the future of this project.


For those of you who were really hoping to get the solution to the Downloads stack drawer problem, it goes something like this. Attach a folder action to your Downloads stack that moves the drawer item out of the drawer and then back in when an item is added. Why don't I have an implementation to give you? Simply, because it doesn't always work. Safari's trick of putting a package down for temporary downloads, then moving the downloaded file out of the package when it's done, seems to confuse the folder action system. Or maybe it's just throttling it, or something. Or it's infinite-looping (firing the action on the drawer icon item coming back). Whatever it is, I sometimes see the drawer pop to the front and sometimes don't. YMMV.

If you really care about this, file a bug with Apple to allow custom stack icons, or badging, or something to avoid the "stack of folders" syndrome seen in the last post.


This week's post contains two short unrelated topics, one interesting and one practical.

For a while now, I've been casually following the birth of the Parrot Virtual Machine. Parrot's goal is to be a general-use virtual machine for dynamic languages, most notably Perl 6 and followed by Python. (Sort of like Microsoft's Common Language Infrastructure, but more dynamic and open source.) As someone interested in high-level language design, there's a lot of cool things here...if only I knew enough Perl to write my own front-end. (You can do it in other languages too, including good old lex-yacc / flex-bison, but you lose out on some of the work they've done for you.)

Anyway, today on the Parrot blog there was a post about (mostly) lockless concurrent garbage collection. The idea is not only pretty amazing to me, it's also more and more applicable as parallelism becomes more and more relevant. The original technique was also published in 1998. (For context, the garbage collectors in Java and Cocoa today are already running on separate threads, and two of the "features" of the upcoming Mac OS X Snow Leopard are Grand Central Dispatch and OpenCL. Parallelism is the way to go.) I haven't yet read the technical papers linked from that post, but there's a very clear summary there that is astounding in its simplicity. (As in, I could probably go implement the simplest one myself without reading the paper.)

Things like this are cool for two reasons: one, that such a neat hack can be done, and two, that Parrot would take care of it for me if I target that as a backend.

That wraps it up for Parrot, for now. Someday I'll get a chance to play with it for real.

A plain stack of...folders? On to the next tip! If you use Leopard's "Stacks" feature, you might get frustrated with the untidy look of your stacks in your Dock...especially if you have stacks of all folders! Fortunately, a solution's been around for a couple of years now: wonderful drawer icons by Yasushi Chida. These icons are actually attached to old resource fork text clippings (what you get if you drag text to the Finder). These are cleverly (?) named with a ".app" extension to keep QuickLook away.

Installing these wonderful drawers is easy: just drag the icon you like to your stack. They're already named with a leading space, so they'll pop to the front of your stack if it's sorted by Name, and they're timestamped in the future to handle Date Created or Date Modified. (Date Added is a little trickier; I'll summarize my solution at the end of next week's post. I'm not just being lazy; it's on another computer.) The problem comes when you accidentally (?) click on the drawer icon.

The application “ System ” cannot be launched.

To fix this, I cannibalized an AppleScript application, a legitimate Mac OS X bundle application, and dropped in a simple script that pops open the enclosing folder (essentially mirroring the "Open in Finder" option at the end of the list). For the icon, I used Iconverter (VersionTracker link, developer's site seems to have disappeared) to extract the icon to a Mac OS X .icns file, which I then place in the dummy application's resources directory. (Preview can pull icons from the clipboard, but it can't save .icns. The developer app Icon Composer can't pull icons from the clipboard — I'm going to file a bug on this.) Here's the source of the executable script at the core of the "application"; you can also download the whole setup and modify it yourself.

#!/bin/bash

container=`dirname "$0"`
open "$container/../../.."

A stack clearly labeled as system-related Now things work beautifully; there are no resources or outdated formats involved, and the drawer icon does something reasonable if you click on it. I don't use stacks for much, and when I do they're almost always in Grid view. But this makes them a lot easier to distinguish—and a lot more aesthetically pleasing as well. Now, if only you could get rid of the icon in the Grid view...


Failing to keep up with my (bi)weekly postings, I return nonetheless to post something of interest to fellow developers. This post is on my latest Unix-shell-of-choice, the Z shell (zsh).

For a long time I was a bash user, actually since a little after the switch from tcsh to bash in Panther. Bash (the "Bourne-again shell") is useful and dependable and doesn't pretend to be something it's not, something I felt tcsh (and the C shell family in general) was occasionally guilty of. But I got fed up with the context-insensitive completions and uncustomizable keyboard shortcuts, at about the same time. And bash just wasn't that customizable/extensible.

Enter zsh. I'd read about it online before, and figured it would be close enough to bash for me to switch to it even without learning new features. But I could turn on whatever new features I wanted.

Rather than narrate my experiences setting up the various dotfiles, getting used to the small differences, and so on, I'll just note that the default zsh support for completion of SVN commands was a little broken, and that there's a replacement in the GVN repository (instructions here -- drop the "Completion/Unix/" from the path, though). Besides that, here are the interesting parts of my .zshrc file.

HISTSIZE=1000
SAVEHIST=1000
HISTFILE=~/.zhistory
setopt INC_APPEND_HISTORY

Zsh doesn't save command history across sessions by default, so this is a pretty simple enabling of that. On the other hand, there's an extraordinarily complex shortcut system for getting lines or even individual arguments out of the history. I am not yet familiar with this and stick to the good old !command and !!

bindkey -e  # emacs key bindings
bindkey "\e\e[C" vi-forward-word
bindkey "\e\e[D" vi-backward-word

Key bindings! The emacs key bindings have written themselves into my fingers by now, so being able to use them in the shell is great. On the other hand, I haven't yet found the right granularity for jumping words, but in the meantime I'm using vi's definition. \e\e[C and \e\e[D are Option-Right and Option-Left, respectively.

setopt PROMPT_SUBST
PROMPT='[%~] %n%# '
autoload colors; colors
# what is this disgustingly complicated bit?
# if the last command failed, print it in red in parentheses
# before the close paren, print the status in black
# make sure to ESCAPE the last command's % and ) signs
RPROMPT='  %(?..%{$fg[red]%}(%15>…>`echo ${${$(fc -ln -1)//\%/%%}//)/%)}`%>>
%{$fg[black]%}%?%{$fg[red]%}%)%{$fg[default]%})' # if you use this, make sure to remove the newline

My prompt is pretty standard: path, username, and prompt character (% for normal user, # for privileged). But I wanted to make good use of the RPROMPT (right-hand-side prompt). Z shell is smart enough to hide the rprompt when you type out that far, so all I had to do was put it together. I'm not going to take it apart for you, but rest assured it took several tries to get right. (The problem is flipping between string data and shell commands -- they look a lot like each other to me and to the expansion system.) The colors module is loaded to populate the $fg and $bg arrays with the right control codes for switching colors.

autoload -U compinit; compinit
setopt AUTO_CD
autoload zmv

The first line loads the amazing context-sensitive completion system, whose surface I have barely scratched. The second allows you to leave out CD and just put in a directory name to go there (useful for dragging folders in from the Finder). The last enables zmv, a smart file mover/renamer that supports batch operations and regular expressions. Very useful, though I still need to look up examples.

[[ $TERM = 'xterm-color' ]] && alias ssh='TERM=xterm ssh'
[[ $EMACS = t ]] && unsetopt zle

Some defensive options. Most ssh servers don't like the "xterm-color" terminal type, so I use the plain "xterm" for my ssh client. And zsh doesn't need to provide its fancy line editor when running inside emacs. (Actually, it apparently causes great problems if you leave this out.)

alias cp='cp -ir' # ask confirmation for overwrite, copy-recursive
alias mv='mv -i'  # ask confirmation for overwrite
alias rm='rm -id' # ask confirmation, remove directories too
alias ls='ls -Fh' # suffixes, human-readable sizes
alias l='ls -l'   # detail mode
alias la='ls -A'  # show hidden files, minus . and ..
alias ll='l -a'   # show all hidden files in detail mode

setopt IGNORE_EOF # don't log out on EOF
setopt NO_CLOBBER # don't overwrite files unless forced

And finally, some simple shell settings. Matters of preference.

That's it for this week. Zsh really is "a better bash". If you've been frustrated by bash's shortcomings, give it a whirl. And remember that there are a ton more features than I've described here — read about them in the man pages, in the zsh manual, or in many other example pages and blog posts online.

chsh -s /bin/zsh


It's been a while since I've posted here, and I apologize to the two people subscribed to my newsfeed. (That may be wishful thinking; I should run it through FeedBurner and find out.) Things may or may not be more regular during the summer.

Today's post is a snarky one; next week should be back to the "cool programming features". It came to mind when viewing a Mac Rumors article, "Snow Leopard Videos Demonstrating Several New Features Surface". You can take a moment to skim the news/rumor, then come back and see if you agree with me.

"Several new features"? In the first paragraph, two of those are minor UI changes, three are potentially welcome but still not that exciting, and the very first has been in Windows for years. I'm not saying "Put Back" isn't useful, but I hope they have the sense not to put it in their marketing campaign. The Spaces and Substitutions "features" are sort of building on older stuff. (Substitutions seems like Office AutoCorrect in a framework and might be turned off as soon as I get Snow Leopard.) The changes to the Finder prefs do not fix the old and new UI problems plaguing that cornerstone app—though I'll admit I do full-computer searches more than home-directory-only ones. And QuickTime X (ten? ex? MR calls it "10") is just a new UI (okay, nice) on QuickTime Pro, which should have been made free a long time ago.

OK, so these are videos, and the article's only about what's in the videos. And hey, didn't Steve Jobs say Snow Leopard would only have one new user-facing feature?

It's not that I mind not having new features. I just don't like old features being repackaged and sold. Especially not "Put Back", since Apple has had a long history of making fun of Microsoft for stealing feature ideas. (Which, by the way, does not mean the feature should not be included; it just means you shouldn't make it a major selling point. Which, AFAIK, Apple hasn't. So okay.)

For the other side of this, see an Apple fan's updated version of a classic Apple ad from 1995 (via Waffle). These are forward-facing features, and it shows one of the problems with Microsoft's current behavior (at least with regard to their OS): despite having a ridiculously high market share, they're playing catch-up to Mac OS X, when they should be parading and upgrading Windows like a leader, with new features.


Next page | Last page | View all

Sort by: Date / Title | Order: Ascending / Descending