I’m getting around to some of my New Year’s resolutions recently, and I’m afraid this weblog didn’t make the cut.
So this will be my last post on Helpful Tiger.
Don’t worry, I’m not disappearing from the Web. I’m sure I’ll see most of you around.
I’ve enjoyed writing it. I hope you’ve enjoyed reading it.
I’ve been writing code for a command-line input buffer.
Seems a bit odd in this day and age, eh? OS X already has a command line implementation – several, in fact.
But this one’s for my home project, which is a curious combination of Web browser and command line tool.
Per the Windows port, this command line acts a little odd. You can move up and move down in the command history using the up and down arrow keys, but if you modify any of the previous commands, those changes don’t “take”. Only changes to the last command are remembered if you move up and down the history.
Oh, and the current command – the one you’re looking at on the screen at any particular moment – needs to be held in an external buffer controlled by the cross-platform engine.
I have an NSArray of all the previous commands, and logic to copy the proper entry in that array to the buffer if the user moves up and down through the command history, but that last command line makes things a bit more fiddly. If you move up, I have to remember the contents of it, even though I have to fill the external buffer with new contents. But I don’t want to add it to the history, because you haven’t pressed return yet.
I said “fiddly,” not hard. My particular solution was to add it to the history array anyway, for the benefits I get from reusing the code that copies from the array to the buffer. As soon as you move up, we enter this special “extra entry added” mode, which we leave again if you move all the way back down again, or hit return.
So I knew that whenever the current command index didn’t point to the very end, I was in my mode, but I found myself getting a little confused, not able to nail everything down, even though the code itself wasn’t very complex. I knew what I wanted, but the code didn’t look like what I wanted.
The solution (and the point of this long-winded entry) was all about naming. Instead of the logic looking like this:
if(mCurrentCommandIndex < [mCommands count])
I made it look like this:
and exiting the mode, instead of looking like this:
I changed to look like this:
Not rocket science, but it allowed me to see where the code was doing its job, and where it wasn’t, and make the appropriate corrections.
Oh, that, and unit tests.
P.S. Yes, I use PowerPlant-style variable prefixes in my Cocoa code. So sue me.
As I’ve said before, for my small home project, where the most interesting work lies at the intersection of AppKit and a cross-platform C++ engine, I can’t write code the “unit test” way – writing the tests first – because those intersections are precisely what can’t be isolated from the entire application.
But I have changed some habits.
Now, I move the little snippets of logic I’ve written from their organically-grown locations to separate stand-alone functions.
These functions are grouped by purpose in their own files, such as
UTF8 Functions.c or
Geometry Translation Functions.m.
Because they aren’t classes, they need all their context passed in as parameters. This can be a pain in the ass, but it also prevents me from grouping too much functionality together; it would be too tedious to add all the necessary parameters.
It also means that I can access the logic directly from unit tests. I can try out in isolation all the little variations I need to really ensure the code is sound.
This works well for a small, home project. Splitting functionality out into lots of additional files may work less well in large-scale endeavors, where there are already hundreds of files in a project.
Then again, in such huge projects, the data model may be elaborate enough that there are more classes that, without modification, can be tested in isolation from AppKit (or each other).
Writing bugs often makes me more productive.
That, or writing requests for help.
For instance, I have a custom NSView, whose contents, mostly text, are completely laid out by a cross-platform engine, i.e. not by Cocoa. So I can’t use NSTextView.
But I want it to act like an NSTextView. For instance, I want to be able to select text just like an NSTextView, and I want a blinking cursor just like an NSTextView.
So I started writing up a query to the Cocoa-dev mailing list about this. But you don’t get much respect, or much help, if you aren’t as specific about what you need as you can be.
So first, I had to determine what I didn’t know. Which meant trying to implement it, and documenting where I got stuck.
Turns out, I didn’t get stuck, at least for the first part.
How do I get started learning about something new? Well, if I can guess an API name, I’ll start with the documentation, but usually I can’t. So I search http://search.lists.apple.com based on the words I do know, like “selected text color”. If there are a lot of false matches from other frameworks, I’ll restrict the search to Cocoa-dev, but sometimes the answer is, say, a Carbon API, so I’m inclusive at first.
I find that the spare, high-level style of Apple’s developer documentation sometimes leaves me scratching my head. Is this really what I need? The mailing lists are great for this sort of clarification.
So I find out the API I need for the selected text background is the appropriately-named:
And the post that gave me that also pointed me to the “Accessing System Colors” documentation topic, which helpfully mentioned the notification:
which allows me to change the color in my application immediately if the user chooses a different highlight color from System Preferences.
But I wasn’t done because, when an application window is no longer foremost, the selection color changes from the user-choosable highlight color to a gray color.
I was unable to find a specific question about that, but I found another post that talked about matching API colors via the Developer color palette in the color panel. (Cmd-Shift-C in TextEdit.) There isn’t a
nonForemostSelectedTextBackgroundColor system color, but there is a
secondarySelectedControlColor that matches the non-foremost selected text color in TextEdit, and has a name that at least could apply to my situation.
So I have something that works, and gets its information as much from the system as possible, so it’s as future-proof as possible while still being custom. And I didn’t need to ask any questions! Except…
The same routine didn’t get me an answer as to the best, most system-friendly way to show a blinking caret. I could do the whole thing myself, but that might look weird in the radically overhauled UI in Mac OS X 10.9 “Puddy Tat,” eh? And I’d rather not have an NSTextView whose sole function is to show that caret – seems like a fragile, hacky solution.
So I have a question after all.
I was getting a strange smearing effect when I tried to scroll in the main view of my application’s window.
I knew my content-drawing routines were being invoked correctly. I knew that the effect seemed to be happening somewhere in the depths of Cocoa, not in my code. But that’s all I knew.
Turns out, it’s because I was setting my view frames to fractional values (they’re floats, after all), but my content engine was still drawing things per integer coordinates.
This is the kind of thing that unit testing won’t help.
My goal was “make resizing the window and then scrolling through the main view work.” Can’t write a test for that because “work” means “look right,” and you can’t test for that when you don’t know how to make it look right yet.
It’s one part experimenting and one part perseverance and one part lightbulb going off after things go wrong.
And I find myself working through these kinds of issues a lot.
Anyone who says “Always write your tests first” can stick that in their pipe and smoke it.
Doctor: So don’t laugh.
“Legacy” in this case means “anything without unit tests,” which means just about all code out there. Including, I’m ashamed to admit, most of the codebase of the Neutrino project I’m currently working on.
Why, after all that effort I put into adding unit test capabilities to my project, have I mostly failed to follow through? Feathers’ book describes it well. Currently, neither programming languages nor external libraries and frameworks make it easy to separate the unique logic of your code from all the dependencies that it requires to actually get its work done.
My codebase, for example, does a lot of custom drawing to a view, and does that drawing in response to calls from the cross-platform engine that actually drives the process. So I’ve got two huge dependencies: the engine, and the Cocoa framework. How do you get around that?
But it’s not just Neutrino. I prefer to work on code that’s in the middle of complicated systems, doing interesting and complicated things.
The classes in Kent Beck’s Test Driven Development: by Example were small data model classes that dealt only with each other. Well, sure, that would be easy! Real world code is rarely so straightforward. Beck describes a world so far removed from mine it might as well be in another galaxy.
Feathers, on the other hand, gets dirty in the trenches. But he doesn’t reveal any magic formulas. Instead, over and over again, he says: stop laughing. Change the code.
Does the class you want to test rely on a well-protected singleton? Change the singleton so it isn’t, even though that allows others to use it incorrectly.
Does the class you want to test have some private methods? Make them public.
Does the class you want to test rely on other classes from a library or framework? Change your code not to refer to such classes directly, even at the cost of added complexity and overhead, so you can break the dependency and use dummy objects instead for your tests.
All these are ugly solutions, design-wise. And Feathers knows it; he makes multiple apologies for his techniques. But he stresses that they work.
Well-designed code, code that only allows itself to be used in the way it needs to be for production scenarios, turns out to be anathema to unit testing.
When I programmed primarily in C++, I looked askance at the typeless nature of Objective-C. How will the compiler check that you’re doing the right thing? What I’ve found since is that very few errors result from this extra freedom from type that you have in Objective-C. Maybe all those safeguards and extra complexity in C++ weren’t necessary after all?
Similarly, I wonder if the next major programming language might be optimized for unit tests. It will have even fewer compile-time restraints, even fewer security restraints, but many mechanisms to make it easier to test what you’ve just written – maybe even require such tests, or auto-generate such tests.
Until then, I’m going to see what the results are when I make my Neutrino code a lot less jolly.
I have a couple of music files in Ogg Vorbis format, a format that iTunes does not support by default.
Before, I used the Ogg Vorbis QuickTime 6.2 component written by Jordan Mendelson. Notice, however, the “6” in that title. Mac OS X 10.4 “Tiger” uses QuickTime 7. If you come to a Ogg Vorbis song file in your iTunes playlist on 10.4 when you have Jordan’s component installed, iTunes crashes.
Even cooler than that, the above page is really a bit of a developer Weblog, describing some of the steps he’s taken to make the component. If he got himself an RSS feed, I’d subscribe. He even has a teaser: “Watch this space.” Guess Apple isn’t the only one keeping secrets, eh?
Note to Macintosh developer community:
The days of the .sit file have passed.
StuffIt Expander isn’t even included by default anymore on new Mac OS X 10.4 “Tiger” systems. You have to download it separately.
Now, I will admit, this is easier than it was even just a year or two ago. Allume Systems (née Aladdin Systems) used to make it incredibly difficult on their Web site to even navigate to the free Expander download. And the download was in fact not Expander, but rather StuffIt “Standard,” which included several additional shareware products most people didn’t want. The philosophy seemed to be, “If we just piss off our users enough, maybe they’ll buy something from us!” Ugh.
When I just tried to download StuffIt Expander 10, however, I was able to get to the download in two clicks, and the resulting installation was Expander alone. So that has improved.
But you’re still requiring your users to find and download a third-party application just to get to your product.
If your product is Mac OS X 10.3 “Panther” and up, the solution is easy: use the operating system’s built-in “Create Archive” contextual menu in the Finder to build a .zip archive of the files you want.
Now, if you’d used the command-line zip utility on Mac OS X 10.2 “Jaguar” or earlier, you would’ve found that it ignored Mac-only filesystem attributes like resource forks. “Create Archive” is smarter than that. If you double-click such an archive in the Finder, it will extract the archive contents, resource forks and all.
The downside? “Create Archive” uses a custom directory layout (presumably in order to organize the extra bits) which will look strange if you attempt to open that archive with a more traditional command-line zip utility, even the zip utility at
/usr/bin/unzip on that same OS X 10.4 “Tiger” box where you made the archive in the first place! (Not to mention the zip utility on 10.2 and earlier.)
This means that these .zip files, which by their suffix look nice ‘n’ cross-platform, really aren’t. They’re still primarily for delivering content to Mac OS X users. Your sole benefit is that the archive will be double-clickable on 10.3 and higher, without additional downloads.
If you need to support Mac OS X 10.2, well…I hear StuffIt Expander is easier to download than it used to be. Oops! You have to go back to StuffIt “Standard” 8.0.2 to get Mac OS X 10.2 support. It looks like they require you to join their mailing list just to download the damn thing. Tough luck….
My advice? Make a .dmg file.
The TADS interpreter scene on Mac OS X is a bit like the browser scene. There are more of them than you’d expect for a minority platform, and they embody different philosophical approaches.
The saying goes: “Cheap, fast, or good: pick two.” I tried “perfect” with my MacTADS project and wound up never finishing it at all, so it’s no surprise that the authors of the following interpreters made a different set of compromises in order to get their applications out the door.
(The observations below are more about the UI than a detailed review of gameplay.)
Second, he built a GUI TADS interpreter on top of that, called QTads. As ANSI C and POSIX are to FrobTADS, Trolltech‘s Qt library is to QTads: if you support those APIs, FrobTADS and QTads respectively can be built for your platform.
This approach led to a full-featured interpreter for the Mac platform without needing someone to commit full-time to it. QTads even has some niceties not in other Mac interpreters, such as a font/color “preview” panel and theme sets.
The downside is that QTads is a Mac application the same way Eclipse is a Mac application. Colorful, but not exactly native-looking:
Huh, an Exit menu item? Never fear, there’s still a regular Quit menu item under the Application menu, but this goes to show we’re not in Cocoa anymore.
As another example, have a look at these two lower right-hand corners of windows:
QTads has an ugly black outline around its main scroll view, and its window grow box is half the size of a native Macintosh window grow box. NetNewsWire’s version is provided for comparison.
Finally, if you resize the window, you’ll notice some flickering. QTads eschews OS X’s double-buffering, and instead erases and redraws the edges of its window right before your eyes.
Some of this might be fixable with more work on Qt, but some of it is a deliberate, un-Mac-like style that comes with the territory.
Cugel’s author made a different set of compromises. Native? Check. Full-featured user interface? Well, not really. For example, there’s no status bar area at the bottom for a “more” prompt. Such a bar doesn’t come native with the Cocoa scroll view, which is why my in-progress Neutrino main window doesn’t have one yet, either.
But what you get instead is a “Unified Interactive Fiction Player.” It can play not only TADS games, but also Hugo games, Alan games, Glulxe-based games (supported by Inform), and others.
It’s actually pretty cool to look in the Cugel package and see the individual interpreter utilities for all these languages.
Ben Hines MaxTADS OS X port
Ben Hines made a third set of compromises. My impression is that in 2002, he saw that there was already a perfectly good OS 9 TADS interpreter, Andrew Plotkin’s MaxTADS. He also saw that Apple provided a quick way to port such Toolbox-based code to OS X, the Carbon APIs.
So he “ported” MaxTADS to Carbon, and by “ported” I mean that he made the fewest changes possible to the codebase and resources so it would build and run as a Carbon application on 9 and X. The UI remained entirely unchanged, and it looks more and more anomalous on subsequent versions of OS X. For example:
Yes, that’s a System 7-style textbox you see!
Here’s another issue. Two quit menus:
The Cmd-Q keystroke shortcut does nothing, by the way. You have to select the menu item by mouse.
The upside of all the hurried work? This was the very first native OS X TADS GUI-based interpreter. The other two interpreters mentioned above were released two years or more after this MaxTADS port.
…Until now. (Cue ominous music!)
It’s for an application called Neutrino, which will allow you to do something on a new Mac 1-2 years from now that you otherwise wouldn’t be able to do.
Word by Word
Neutrino is a TADS multimedia interpreter written in Cocoa using Xcode. Let’s break that down:
TADS: “Text Adventure Development System,” a special-purpose programming language, library, and virtual machine for both writing and playing interactive fiction a.k.a. text adventure games. Wikipedia has some history on it.
multimedia: When TADS was first introduced, it was strictly T, for textual. Its author, Michael Roberts, eventually decided to add some multimedia capabilities to it. He called it HTML-TADS, because the way to add these elements was via a simplified and customized form of HTML markup. There are only two interpreters that can fully display such content, the TADS interpreter for Windows that Michael Roberts himself wrote, and HyperTADS for the Mac, by Iain Merrick.
interpreter: TADS source code is not compiled to native CPU instructions, nor does it use specific operating system APIs. Instead, it’s compiled to be played in its own virtual machine maintained by a platform-specific interpreter, like Java source is compiled to run on a Java virtual machine.
Cocoa/Xcode: Because Neutrino is written using these technologies, I will be able to release a “universal binary” version of Neutrino, that will work on both PowerPC Macintoshes, and the Intel Macintoshes that will be released over the next several years. (Carbon/Xcode would be another possibility.)
For TADS’ early history, the sole Macintosh interpreter was also maintained by Michael Roberts. It used the early Toolbox TextEdit APIs, though, so it was limited to 32K of text scrollback. Plus, it didn’t display any styled text at all.
Once TADS went open source, Andrew Plotkin wrote a new interpreter for it, called MaxTADS. It had all the bells and whistles and matched in both style and name his interpreter for Z-code games, MaxZip.
When HTML-TADS arrived, HyperTADS was written to handle it, since MaxTADS remained text-only.
This all happened before OS X was released.
Today, there are three native TADS interpreters that I know of for OS X. Ben Hines did a quick-and-dirty Carbonization of MaxTADS in 2002. Recently, the more substantial ports QTads and Cugel have been released, and are under active development. I do a more thorough review of these interpreters (or at least of their UI) in this post.
So the native interpreters won’t display multimedia game content, and the one Mac multimedia interpreter is non-native and won’t run on Intel Macs.
Answer to the Riddle
If you buy a new Mac 1-2 years from now, and you want to play HTML-TADS games like Six Stories or Arrival or Exhibition to their fullest, you will be out of luck, unless I get my ass in gear and finish Neutrino.
Oh, yeah, one last note: it’s not finished yet. In fact, it’s probably about 10-25% done. But here’s a screenshot: