AntiRSI 1.4njr4

Since releasing AntiRSI 1.4njr2 I’ve made a couple more minor releases.

1.4njr3, released on November 5, fixes some graphical rounding issues reported by Andy Reitz; the times were displayed as 3:01, 2:00, 2:59, etc. I was really surprised I never noticed this myself.

1.4njr4 shows the break window in all Spaces on Leopard; of course you could do this with the Spaces preference pane, but doing so by default makes sense. It also stops AntiRSI from getting focus when you hit the “Postpone” button (Cocoa’s API for this is…interesting). Or, if you have AntiRSI set to force itself to the front during breaks, it’ll bring your previous app to the front when the break is finished so you can resume work.

I’ve made some Spaces-related changes for Pester, too, and will try to get beta 8 released one of these days. Happily, my research is going really well at the moment and I don’t want to disturb my focus.

Download AntiRSI 1.4njr4 here, or just use the “Check for Updates…” item in the AntiRSI menu if you’ve already got my version of AntiRSI installed.

Pester 1.1b5 released

Pester 1.1b5 is out. If you’re wondering where betas 1–4 went, they were released privately…in 2003. Looking back at my weblog posts from May 2003, I see my comment about my parents “finally finishing up renovations on their house in Boston”—four years later, they’re still living in a single room of that house, though they continue to make steady progress on fixing the disaster left by their contractors.

For various reasons I didn’t pick Pester back up until this weekend, because NSDateFormatter broke natural language date parsing for Pester 1.0 in Leopard. Pester 1.0–1.1b4 ran on Mac OS X 10.1 or later; Pester 1.1b5 requires Mac OS X 10.4 or later. It was a bit sad to rip out painstakingly developed workarounds for old Cocoa date parsing bugs, but good to see that the new ICU-based formatters basically work, even if they completely lack any natural language parsing ability.

In an effort to get something usable on Leopard released, this version has some features disabled because they aren’t stable enough yet (QuickTime playback, speech), don’t work at all (dock bouncing) or are untested (wake from sleep). The user interface is still mostly what I came up with back in November 2002 (wow, Jaguar really was that ugly).

Among the changes that aren’t from 2003 are my first use of Sparkle:

Subsequent updates should therefore be simple.

Back to the reason for this update: NSDateFormatter in pre-10.4 mode used to understand “tomorrow”; in Leopard, it doesn’t, despite being documented as doing so.
There seems to be a severe lack of open source libraries for natural language date parsing—all I found was chronic (Ruby, English only); jchronic (Java port of chronic, English only) and Date::Manip (Perl, multilanguage).

I chose Date::Manip.

Yes, this means Pester now embeds a Perl interpreter.

Perl’s embedding API is interesting, but it works, and aside from the memory usage, nobody has to know. I don’t actually have non-English parsing in this version yet, because I ran into some weird bugs in Date::Manip; I’ve emailed the author but haven’t heard back yet.

I’m otherwise in the middle of the final work for my PhD, so I can’t guarantee 1.1 final will come out soon, but I think it’ll be less than 4 years!

AntiRSI 1.4njr2

Onne Gorter’s AntiRSI is a Mac OS X application which reminds you to take periodic breaks from typing. Along with my beloved IBM model M15 keyboard, it’s essential to my getting any work done at all.

However, AntiRSI had some problems. The animated progress bar displayed during breaks sucked CPU like crazy, causing problems if I was watching a video at the time. Breaks kept getting prematurely reset because AntiRSI thought I was typing when it was a program like VLC calling UpdateSystemActivity, trying to avoid triggering the screensaver. Finally, the break window was just plain ugly—look at those poorly antialiased corners.

The last problem was more of a missing feature. While programming, I spend enough time thinking that I’m generally pain-free even if I work all day; unfortunately, debugging, writing and system administration can be much more typing-intensive, so I have to limit my daily typing time in aggregate, but AntiRSI didn’t keep track of it.

Over the past few years I’ve fixed these issues, and since my previous emails offering patches have gone unanswered, thought the result might be useful to others: download AntiRSI 1.4njr2 if you’re interested.

Some screenshots:

Session timer in AntiRSI 1.4njr2 AntiRSI 1.4njr2 Preferences

If you have a Mac and don’t like AntiRSI, try Time Out: it’s much more customizable, but takes over the entire screen, making it unusable for me. According to a VersionTracker review, it also hogs CPU during breaks.

For Windows and X11, check out Workrave; it’s great, includes a session timer and even comes with network support if you use multiple computers.

Internationalization, services and ICeCoffEE

While taking a break to work on ICeCoffEE today, I discovered an annoying internationalization bug.

First, some background. Since version 1.4, ICeCoffEE has let you hide items in contextual Services menus. Peter Hosey covers the feature nicely here.

Service menu items can be localized, like almost everything else in Mac OS X. The service provider apps include one or more language translations for their item names. The user configures a list of languages in order of preference in the International System Preferences. And the service-invoking application—the one from which you pull down or pop up the Services menu—also has a list of languages in which its user interface is localized.

Mac OS X makes the (sensible) choice that the service-invoking application’s user interface language dictates the language of all text in that application. But the fact that we’re dealing with ordered lists of languages, rather than individual languages, makes things a bit more complicated. Let’s take an example:

Your user interface language preferences are, in order, Japanese, German and English.

(Those also happen to be the languages I understand in ascending order of proficiency, with a gigantic gap between Japanese and German.)

Service menu item A has Japanese, German and English localizations. Item B has only German and English.

Say the app you’re running has only an English localization. Items A and B both appear in English.

What if your app has a Japanese localization? Item A appears in Japanese, but item B appears in German, even if the app has no German localization.

I’m guessing (have not completely verified) that OS X picks a list of potential service menu item languages by looking at the user’s preferred language list, starting with the user interface language of the current application.

What’s the bug? ICeCoffEE’s configuration sheet is hosted in System Preferences. When looking for a list of filter-able services, ICeCoffEE asks System Preferences for its Services menu and saves your choices according to the displayed item text. See for yourself:

% defaults read net.sabi.icecoffee ICServiceOptions
{
    ChineseTextConverter = {ICServiceHidden = 1; };
    "CocoaMySQL-SBG" = {ICServiceHidden = 1; };
    "Define in OmniDictionary" = {ICServiceHidden = 1; };
[...]

I did this because it seemed to be a WYSIWYG method of filtering, but it means that the user interface language of System Preferences influences what service names you see. If an item is also localized in a different language, it won’t get hidden.

I’ve never received a report of this bug. However, if it affects you, I can suggest two workarounds.

  1. Edit the preferences property list yourself. The format is pretty simple, but I don’t recommend this. If you break it, you get to keep both pieces.
  2. For every user interface language you use, launch System Preferences with that language at the top of the preferred list, and select the localized service names for that language in ICeCoffEE’s preferences. This assumes, of course, that System Preferences is localized in every language for which a service item you want to hide is localized. At worst, you will end up with something like this, where ICeCoffEE’s interface is in German and the service names are in Japanese:

    i18n.png

    (Sharp-eyed people may notice two changes in the screenshot above, which hint at features coming in ICeCoffEE 1.5 :-)

I plan to fix the bug by doing something like option 2, adding the text of every available service menu item localization to the ignore list. It makes things more complicated because I currently store hidden services hierarchically, such that if you disable the submenu, everything inside it goes away; however, it’s possible that different localizations of service names will have different submenu structures. (Yuck.) This method also breaks if a newer version of an application adds a service name localization, but simply opening and closing the ICeCoffEE configuration sheet should fix that problem.

There are other ways I could handle this. I could store the bits used to define a service (NSMessage, NSPortName, etc.) but that would mean a lot more work to do when filtering the service list without scanning the disk as Service Scrubber does. Or I could canonicalize to English before filtering, which conflicts with applications that have no English localization, or applications like Monolingual which delete localizations—although deleting English is often unsafe because it’s the ultimate “fallback” language.

An AppleScript to update podcasts and your iPod

These days nearly 100% of my iPod listening is podcasts, and iTunes’ iPod-syncing podcast support is decent but lacking in a few areas.

One issue that bugs me is that, after connecting my iPod, I must update podcasts then update the iPod again in order for the already-listened episodes to disappear from the iPod. While the play counts update on the first, automatic iPod sync, the associated action (removing the corresponding episodes) doesn’t happen until you do another podcast update.

Another issue is the update timeout—after a few days if you haven’t played an episode of a podcast, it’ll stop updating. For people like my father who abandon their iPod for months at a time (hi, Dad!) it is a great bandwidth-conserving idea, but for me, with video podcasts and a non-video iPod, I like to accumulate episodes to watch when I’m exercising at home.

One of Doug’s AppleScripts for iTunes handily solves the second issue, and I’ve written a script to deal with the first. iTunes doesn’t let you query its iPod update status, so I instead wait for iTunes to unmount the iPod. I found this script which periodically checks the iPod disk space usage, but it fails for me, probably because iSync takes a long time to often do nothing.

Embedding this bash script into AppleScript was a real pain, what with do shell script’s many and varied limitations, as I had to remove line breaks, indentation, my use of extended globs and even the while loop, because iTunes failed to finish updating the iPod while the script was executing. I think I will be checking out the newly revived PyOSA so I can do all this in Python instead.

Download the AppleScript, unzip it and place it in ~/Library/iTunes/Scripts.

(Note: this script is only tested on my Mac with my 3G FireWire-connected iPod; it definitely won’t work if you have iPod disk mode turned on, and may not work on other configurations.)

ATSServer really, really, really sucks

The biggest day-to-day annoyances I’ve had since 10.0 have been, in descending order of irritation:

  • Fonts: inconsistencies between rendering paths, bad support for bitmaps (I still can’t use my favorite font in BBEdit), ATSServer hangs/crashes, silent refusal to activate, font cache corruption, worthless Font Book interface, etc.
  • Disks/filesystems: HFS+ slowness, Spotlight flakiness. AFP instability, slowness and complete inability to handle concurrent accesses. Disk Arbitration flakiness. Disk imaging instability and yet more flakiness. Flaky network browsing. Still no LVM. ZFS on OS X can’t come too soon—it’s been a joy to use on Solaris.
  • USB: crashy drivers (less so of late), poor transfer throughput, overly aggressive port deactivation and poor feedback when something appears to go wrong. Some of these problems might be hardware—this Intel mini doesn’t work with my external USB 2 enclosure, whereas my iBook worked fine.
  • Finder (need I say more?)

Today alone I spent about an hour troubleshooting font problems. First I spent about four reboots trying, and eventually failing, to get the fonts in ~/Library/Fonts to activate. No amount of font cache trashing or safe booting fixed it; I eventually just renamed the folder and told FontExplorer X to import them, at which point everything worked… until half an hour later when Camino hung then Terminal hung (as did every other app I tried, such as Dock and LaunchBar). Turned out ATSServer was using 100% CPU in some C++ destructor. I had to SSH in from another machine to kill ATSServer, at which point everything started working again. Guess I should be glad that it worked, or something.

I wonder how bad the underlying code really is, and pray it’ll get some attention in Leopard.

launch 1.1 released

launch 1.1 is out. I started working on it back in March, but after rewriting most of the launching logic only to find out that the feature I was implementing (command-line arguments) didn’t work because of an Apple bug, I got a bit disheartened. Finally, some email prodding from Peter Hosey, the Intel mini I got last week at school (thanks to my advisor), and good old-fashioned burnout after getting back from Portland caused me to finish it up.

Changes in this version:

  • -L: send “launch” (ascr/noop) event to app, bypasses automatic opening of untitled document, etc.
  • -o: pass command-line arguments (still broken)
  • display content type ID (UTI)
  • display architecture of Mach-O files
  • switched to new LSOpen APIs (now requires Mac OS X 10.4 or later)
  • switched to new date formatting APIs (the old ones are deprecated)
  • for compatibility with open, take app path as argument to -a
  • Universal Binary, compatible with Intel Macs

Sample output demonstrating some of the new features:

% launch -f /usr/lib/libSystem.B.dylib
/usr/lib/libSystem.B.dylib: document
        type: ''        creator: ''
        architecture: PowerPC, PowerPC 64-bit, Intel 80x86, Intel x86-64
        kind: Unix Executable File
        content type ID: com.apple.mach-o-dylib
        data fork size: 7.62 MB on disk (7983880 bytes used)
        created: 9/29/06 6:50:49 PM
        modified: 9/29/06 6:50:49 PM
        accessed: 10/30/06 5:19:11 AM [only updated by Mac OS X]
        backed up: 12/31/03 7:00:00 PM

Now to crawl back into my hole and attempt to finish this paper…

Odds and ends

WWDC was fun, if exhausting; everything went wonderfuly smoothly (ignoring the traffic on 101 driving into SF on Friday night) until my flight home, when we were delayed four hours while United maintenance drove two bolts from SFO to SJC. Joy.

For some reason I didn’t get too much out of the WWDC sessions; perhaps I picked the wrong ones to attend. The small amount of time I spent in the labs made me wish I had hung out there more. I got to meet lots of people, both among my friends and the Mac developer crowd, and have some interesting conversations—although, as usual, even with thousands of people surrounding me from time to time I felt completely isolated.

I haven’t finished organizing photos and videos yet, mainly because I’ve been so busy since I returned, Lightroom really likes a faster machine than my iBook, and my iBook died over the weekend in any case. The photos I took at the large and enjoyable #macdev dinner are here, however.

There’s now a date and Web page with the papers for the OOPSLA 2006 Dynamic Languages Symposium in Portland in October, at which I’ll be presenting the work I did on a hardware transactional memory-enabled PyPy. Half the papers are Python-related, which is rather cool.

Finally, a couple of zsh tips. The great thing about a shell with as many features as zsh is that you never stop learning about new features and ways to use them. The annoying thing is that you seem to forget things just as quickly. Until this morning as I came across a mention in the zsh book (which I highly recommend), I’d used this idiom to get all the directories under the current one:

% print ./**(/)
./bin ./eio ./inputs ./outputs ./results ./src

But this excludes the current directory, which I often want (and usually forget) to include. Instead, you can do the following:

% print ./**/
./ ./bin/ ./eio/ ./inputs/ ./outputs/ ./results/ ./src/

which, in addition to being shorter, has more DWIMitude.

This one is probably a bit better known, but I also discovered yesterday I can get a directory history list by typing ~- followed by the tab key. Reverse the sort order with ~+. Either way, it beats typing popd repeatedly to find the directory you want.

Packing for WWDC

The next ten days are going to be pretty hectic, as I’ll be staying at no less than 6 different places. I’ve been cleaning up, doing laundry and packing all night, and along with my clothing and electronics I’ll be sure to take with me the most important items of all:

Mike McCracken in my phone

More info at Mike McCracken’s blog.

Another day, another WordPress upgrade

Running WordPress 2.0.4 now. The upgrade took longer than expected, but I’ve documented what I need to do for next time, and upgraded a couple of my plugins as well. Hopefully nothing broke.

The ICeCoffEE 1.4.3 release went pretty well—I’ve received two crash reports, and one user claimed it didn’t work for him. One of the crashes is not my fault: Safari crashed during a page load triggered by a Command-click. For the other one, I discovered that I had completely stripped the distributed binaries, which isn’t too useful for debugging:

Thread 0 Crashed:
0   com.apple.CoreFoundation       	0x90853b76 CFBundleCopyLocalizedString + 106
1   net.sabi.ICeCoffEE             	0x002e5358 APEBundleMainLateLoad + 23883
2   net.sabi.ICeCoffEE             	0x002e569b APEBundleMainLateLoad + 24718

I haven’t yet figured out how to map those addresses back to the source code, so I’ll repost 1.4.3 with symbols later this weekend. It’s now less than a week until I leave for WWDC, the pace of my research work continues to quicken and I’ve got a lot left to arrange.

Next Page »