2005-07-30

Book: "Hacking Mac OS X Tiger"

This is a weird book. If you look at the publisher's web site, or the synopses provided by on-line booksellers, you're likely to be confused. I heard about the book in a post by Jon Rentzsch where he mentioned that one of the chapters is about mach_override and mach_inject. A book with that kind of content sounded like an interesting book, but when I looked on the web, the details I found suggested that the book was actually about using System Preferences and iTunes playlists and other "Mac OS for Grandparents" stuff.

At this point, you might be expecting me to tell you which kind of book this actually is. Hard-core programming along the lines of injecting code into other running processes, or obvious stuff about using the Finder's "Show View Options" dialog?

The funny thing is, it's both.

The blurb describes Knaster as a programmer, but none of the programming chapters are by him, and Google gives the impression that his Mac programming days were back in the Mac dark ages, when Mac OS was a crufty home-computer OS and Mac programming was all about Pascal and 68000 assembler. More recently he seems to have written books on using applications such as iTunes. (I'm not saying he can't program. I'm saying I don't know of anything he's written.) So maybe this explains the book's schizophrenia — he wrote the chapters on applications, and his friends wrote the chapters on programming.

It doesn't explain who thought this mix would make a good book, though I have to admit that it almost works.

This book is divided into three sections: "Tips", "Mods", and "Hacks". All the way through, it helps to remind yourself that these people were Mac users when that was something to be ashamed of, and the rest of us were using the commercial Unixes of the time.

Tips
The tips section is really just about playing with the configuration of the Finder, Dashboard, Dock, et cetera. Some of the stuff is trivial: I wasn't joking when I mentioned using the Finder's "Show View Options" dialog. Other stuff is a little less obvious; stuff that's in the UI but that I'd never noticed. And some stuff is more hidden; the kind of thing you see on macosxhints.com that involves using defaults(1) to modify hidden preferences.

In places, this part of the book reads rather like Daring Fireball. Full of things I didn't know about the "option" key, for example. I wonder if the author spent a week option-clicking and option-dragging everything he could find, in an effort to come up with a catalog of uses of "option".

One of the most distressing parts of the book is where it explains how to enable root logins and – worse still – about logging in as root at the login dialog, rather than using su(1). There are two habits I've had to shake as an old-time Unix user. One is that I should use ssh(1) instead of telnet(1), which is easily fixed by not offering a telnet service. The other is using sudo(1) instead of su(1). Logging in as root from the login dialog is worse still because everything else you start will automatically be running as root. Not being able to log in as root is a great way to kick the su(1) habit, and it seems irresponsible to tell people how to have unprotected sex without explaining that there are very good reasons why they might want not want to rush out and try it. They might decide they don't care, or that they think the advantages outweigh the disadvantages, but the book doesn't encourage an educated decision.

Chapter 7, "Nifty Utilities", talks about two shareware utilities and three free utilities. A weird thing about the Mac world is that shareware is still alive and kicking. It seems a bit off to be advertising it in a book, though. And the specific applications chosen are strange until you remember that Knaster is from the Mac dark ages. He's not a Unix person, so the idea of having crappy GUI utilities to mv(1) or chmod(1) large numbers of files perhaps strikes him as useful instead of laughable.

Maybe he's sufficiently scared of the shell that he really thinks checkboxes labeled "R", "W", and "X" under headings such as "Group" are more convenient than chmod u+rw g+r o+r '/Users/scott/Pictures/Picture 1.pdf'? (Or maybe he's only ever seen old timers who insist on using octal, even in 2005?)

Chapter 8, "Running Unix Applications" was a big disappointment. It mentions Fink, DarwinPorts, and GNU-Darwin, but doesn't talk about their similarities and differences, and why and how you'd choose one over the other. He devotes more space to figure 8-3 "X11 looks like this when you start it up" showing an empty desktop with a single empty xterm(1). There follow two more half-page figures showing the same desktop but with xclock(1) and xlogo(1). The chapter picks up a little by ending with mention of X11 forwarding with ssh(1), but it still feels like a squandered opportunity.

Chapter 9, "Terminal" is a joke. One third is about bash(1) (without giving any reference to somewhere you could learn more than using the history and the existence of tab-completion), and one third is about the various games that emacs(1) offers. It doesn't even mention "Secure Keyboard Entry", or the existence of "Find", which is probably obvious to dark-age Mac users (why wouldn't an application have find functionality?) but non-obvious to Unix users. The rewrapping when you resize probably falls into this category, too, though a user's at least likely to discover that by accident.

Chapter 10, "Shell Commands" is a bit better, though he undersells Activity Monitor. It's nice to see him compare and contrast top(1), but Activity Monitor is much more functional. If this were my book, chapter 1 would have been "How Do I Get A Terminal Window?" and "Why Should I Always Have Activity Monitor Running?".

Mods
This section is about messing with existing applications in superficial ways. There's a tour of the contents of an application bundle (a Mac OS application is a directory structure rather than a single file), and an introduction to property lists and defaults. There's a chapter on Automator, Apple's latest investment in the Concorde fallacy known as AppleScript.

The last chapter, despite being called "Xcode and Other Tools", barely mentions Xcode's name before moving on to showing simple nib modification with Interface Builder, and giving an example of using Quartz Composer.

Hacks
The first hack is about putting badges on Dock icons, like Mail does for its unread count. Note that it's about putting a badge on your own application, not an arbitrary other process. (Which would have been cooler.) This is already fully covered on the web and in Joe Zobkiw's "Mac OS X Advanced Development Techniques".

Next come "Word of the Day" and "Best-Selling iTMS Songs" Dashboard widgets. Dashboard. Yawn.

Chapter 18 "Video Desktop" was something I was initially interested in, until I saw it was using the antiquated (and deprecated) QuickDraw API from the Mac dark ages. As the chapter itself admits at the end: "Try to find a way to use OpenGL instead of QuickDraw to do your processing. To accomplish this, you probably have to replace or reengineer everything in the MyQuickDrawView code".

Chapter 19 is about services, but they're much better covered in Anguish et al's "Cocoa Programming", which you must have if you're interested in Mac programming. (It's the best by far.)

Chapter 20 shows how to write an XML importer for Spotlight. It's perhaps most interesting for its coverage of the Spotlight command-line utilities. I didn't spot anything in this chapter that wasn't in the recent macdevcenter.com article. I'm still at a loss to think of a file format that needs an importer that doesn't already have one, and until Apple stop Spotlight from spinning up my iPod (and freezing the UI for those few seconds), I'm unlikely to start making any non-accidental use of it anyway.

Chapter 21 explains how to write the generalization of those crappy GUI wrappers for command-line tools that were mentioned in an earlier chapter. Despite the pointless application (I mean, who needs a window to choose ls(1) options, really?), there's a hidden gem in this chapter. Maybe I haven't been paying attention, but I think this is the first time I've seen an example of programmatically-generated UI in a Cocoa program. An odd omission, when you think that almost all real programs need to do this kind of thing. The example uses absolute layout, but that's better than a kick in the plums.

Some of the code is in Objective-C++, but it's hardly setting a good example. Memory leaks and fixed-length buffers and getline and, well, it may as well have been C but for the use of std::string.

The chapter also shows the magic for starting a GUI application from a Unix-style executable rather than an application bundle. If you wanted to write a Mac OS-only version of zenity(1), this chapter has most of what you need to get started.

And finally, the chapter on mach_override and mach_inject. Sadly, there's no more information in the book than on the web. It's a pity, because I was hoping to see a practical example for once, rather than the usual DisposeWindow+Beep example.

Summary
I found this book mildly interesting. If you're a developer and own a Mac, you probably want to take a look. It's not a good introduction to Mac OS for developers, and it's not a replacement for Anguish et al's "Cocoa Programming", either, but it's interesting to flick through.

The "tips" section doesn't talk at all about how you'd find any of the hidden preferences yourself. A true "hacks" book, I think, should talk about basic Mac reverse engineering skills. They're not hard, but they aren't all the same as traditional Unix skills.

It also doesn't show a Unix developer how to get comfortable with Mac OS. I can still remember the first time I booted Mac OS X thinking "where do I click to get a shell window?" and worrying that Mac OS was going to suck if it was going to make that most fundamental of tasks difficult. More of this kind of thing would have made the book much more interesting.

As with "Swing Hacks", I think the use of "hack" in this book's title was a mistake. In what way is writing a Dashboard widget a "hack"? How about using the Finder's "Show View Options" dialog? This is a sense of "hack" that's completely new to me.

2005-07-26

NetNewsWire / Konfabulator

NetNewsWire crashed on me the other day. Looked to be WebKit's fault, having trouble with Google maps. When I restarted it, it re-opened all the tabs I'd had previously. It did lose the last minute or so of read marks, but the surprise of not losing my tabs pretty much made up for it. Why don't all browsers do that?

It's not like they never crash.

NetNewsWire is starting to get slow for me on my 2001-vintage PowerBook. I'm using the tree-like display, and on feeds like slashdot, where there are 10-20 items per day, it now takes a couple of seconds to switch to that feed. Slower feeds are less problematic, but it's only a matter of time. Hopefully Apple will come out with some new portable hardware less dull than today's new iBooks before things get too bad.

-*-

I see Konfabulator's now free. I've totally stopped using Dashboard: it's slow, awkward (because it's out of the way), and not very useful. I was curious to see what "widgets" Konfabulator offers, given its several-year head start. I thought it might give me an idea as to whether Dashboard might some day be useful.

So I waded through over a thousand widgets to be able to tell you what the future of Dashboard looks like. And the answer is lots of clocks, lots of book/movie/TV-specific countdowns, and lots of traffic webcams. And, for some reason, a lot of widgets to show your IP address.

I've no idea who needs a constant up-to-date display of the US national debt, or an estimate of the global human population.

It seems likely that I've got this whole thing wrong. This junk isn't supposed to be useful. It's just supposed to be. I don't have pictures on my walls, rugs on my floors, or any of that crap, so why should I expect virtual knickknacks to appeal to me? I don't have a sexy robots background image on my desktop, so why would I want an icon of a Japanese schoolgirl that sits on my windows' title bars?

Presumably those people who do fill their houses with cruft are equally keen to clutter their desktops.

The traffic webcam nonsense is still criminally stupid, though. Unlike the hundreds of clocks, most of which appear to have had significant effort put in to their appearance, the traffic webcams all look the same to me. Presumably only the source URL really differs.

I still think weather forecasts and countdowns would be most useful in whatever calendar program one's using; iCal in my case. It knows where I'll be on any given day, it knows what events are coming up, and it's where I go whenever I have a date-related question.

The only widget I saw that I might actually have a use for was a package tracker. But really, I want something that reads my mailbox for me, extracts the tracking numbers for me, and – at the risk of being accused of having some kind of calendar fetish – maybe the best place for it to display its information is in my calendar?

2005-07-17

Book: "Exceptional C++ Style"

I'm a Java programmer. I might have been employed as a C++ programmer for the last 5 years, but I still write a lot of Java, I still care more about Java, and I still think like a Java programmer. Non-type template parameters? Why would you give up the run-time configurability?

The main thing I've got from working in C++ is greater respect for the zero-overhead principle. (In Stroustrup's own words: "what you don't use, you don't pay for" and "what you use can be implemented without overhead compared to hand coding"). I like the idealism of "let's do the right thing, and then worry about how to reduce the overhead", but I've come to rather like the honesty and discipline that the zero-overhead principle encourages. It can be a useful additional constraint when I'm struggling to choose between alternative solutions.

This doesn't change the fact that C++ is a pain in the ass, and that's the reason why there are so many books about avoiding C++-induced rectal trauma.

When I first came (back) to C++ after Java, I read "Effective C++", "More Effective C++", "Exceptional C++", "More Exceptional C++", and "Effective STL" in what seems like quick succession. I may have disliked both authors' weak attempts at humor, but the books were useful and interesting.

It used to bother me that the authors didn't seem particularly critical of misfeatures in the language or library; it offended me slightly that they gave the impression of being the kind of people who delight in the obscure and tricksy. The last kind of person you want to have to work with. But these are also probably the kinds of people most likely to be attracted to C++, to stick with C++, and to have a desire to write books about C++.

"Exceptional C++ Style" seems a little more reflective than the C++ books of old. It does contain items that are the old-style "here's some code; spot the mistakes". Item 1 "Uses and Abuses of vector", for example, which gets the book off to a boring start. There's also an increasing amount of sameness. We've seen this very item before, if not in one of Sutter's other books, then in "Effective STL".

Items 2 and 3, though, suddenly get much better. They take you on a tour of the alternatives to sprintf, and the trade-offs. Best of all, there's even mention of boost::lexical_cast. As I've said before, C++ without boost is like crossing the desert without a camel. It's nice to see it given the same status as the rather basic standard library.

Items 9 and 10 cover export, and why even if your compiler implemented it, it's not what you want anyway.

Item 13 "A Pragmatic Look at Exception Specifications" is a C++ analog to the Java advice "don't use checked exceptions". Hopefully whoever designs the next successful language will have used C++ and/or Java enough to realize that this is another of those ideas that seems great on paper but just doesn't work out in practice. And this is what I mean when I say this book seems more reflective than earlier similar books: it's able to take a long look back at something that's been implemented and used for years now, and ask "how did that work out?".

Two highlights of the middle section are item 18, an argument against public virtual (non-final, in Java terms) functions and item 19 on enforcing rules for derived classes, including the static-typer's guideline of "prefer compile-time errors to run-time errors". A belief C++ and Java programmers have in common.

Then there's a whole section of "Traps, Pitfalls, and Puzzlers"; exactly the kind of thing I don't like.

The book ends with a section of "Style Case Studies", which are much better. It's one thing to hear "know the library", "prefer iterators", "use standard idioms", "be compatible with the STL", and so on over and over again, but it's much more interesting to see actual code be improved. I particularly liked item 35 "Generic Callbacks", which inspired me to spend several evenings coming up with the best, safest C++ wrapper possible for dlopen(3) and friends. (The source is in salma-hayek if you're interested.) Item 36 "Construction Unions" taught me that all names containing __ are reserved, no matter where in the name the double underscore appears.[1]

The last four items take a close look at std::string, the basic premise being a Java anathema: that as much functionality as possible should be provided by non-friend non-member functions. (Or, conversely, that a class should have as few member functions as possible.)

It's interesting that interpreting the zero-overhead principle solely in terms of performance, and ignoring the cognitive cost of having lots of member functions has lead to std::string being one of the largest, ugliest classes you've ever seen. And the real beauty of it is that it's not very functional. Where Java has stuff that's actually useful, such as startsWith, endsWith, replaceAll, and matches, C++ tends to have four different forms of the same function because it wants to support several distinct kinds of argument. So instead of insisting "thou shalt use two iterators", we also have (const char*, size_t), (const basic_string&, size_t, size_t), and (char, size_t) forms of various functions. Ugly. Unnecessary. Confusing.

(Of particular annoyance to me is that Java, with no particular tradition of begin and end iterators, uses begin and end indexes for substring operations yet C++ uses a begin index and a count. And in functions such as std::copy, as Sutter points out, it's actually a count followed by a begin index. Perfect.)

What I don't understand is why Sutter thinks that you need erase, insert, and replace. I've always liked the way that "replace" is the only mutating operation you need on text. (I also didn't understand why Sutter felt that the (size_t, char) form worthy of a special case to avoid the (size_t, char) constructor. Why couldn't we just use a special-purpose iterator rather than insist on actually having a char[] to iterate over?)

It sounds odd to a Java developer, to hear someone campaigning for non-friend non-member functions. Almost like a call for a return to C programming. Dig the 70s-style global functions, baby! But when you look at the mess made of std::string, you can see the guy's point. And overloading and templates make it more a reasonably sensible proposition in C++. You could also do this with a mixin class, though, thanks to multiple implementation inheritance.

Welcome to C++, a Larry Wall world where there's more than one way to do it.

Why would you choose the globals over mixins? The one reason I can think of is that with a mixin, the implementor has to have the mixin available, and realize that the mixin would be useful. A global function is a global function. No invitation necessary.

I wonder how it's supposed to work in stuff other than standard collection classes, though? I'd like to see an example of a GUI program written in this maximally non-friend non-member style. What would it look like? Would it be useful?

In the meantime, I wish I could write define private member functions in C++ that I didn't declare as part of the class. Objective C++ lets me do this (because I don't have to declare the class all at once; I can have part of its declaration in the .mm file). In Java, I'd like a kind of static import (damn, they already used that name!) similar to Objective C categories where a non-instantiable class full of static methods can be imported, and the methods become available as if they were methods on the class of their first parameter. For example:

// StringExtras.java
class StringExtras {
public static int parseAsBase(String s, int base) { ... }
}

// C.java
import static StringExtras;
class C {
int m(String s) {
return s.parseAsBase(2);
}
}

Why does Objective C seem to have had so little influence, despite having a lot of useful ideas?

Anyway. What was I doing before I started ranting? Oh yes: I was talking about "Exceptional C++ Style". Interesting book. I'd like to see some more reflective books on C++. Those were the high points of this book. Puzzles bore me. Or worse, they frustrate me when I can't just fix the design flaw that gave rise to them.

[1. That paragraph used to end with the sentence "Something Sun's dtrace(1) developers don't appear to know". I was referring to an example of "Putting developer-defined DTrace probe points in an application", but Bryan Cantrill points out that "DTrace is written entirely in C; the libdtrace API is entirely an extern "C" API, and we don't support USDT probes in C++. (Due to regrettable restrictions around the extern "C" construct.) The C++ naming conventions therefore do not apply to us — and there is no such restriction around __ in the middle of C identifiers." If you check the C standard, you'll see he's right. I'm not sure why this is missing from annex C, "Compatibility", of the C++ standard. Anyway, my apologies to the dtrace(1) team.]

2005-07-12

Book: "Swing Hacks"

If you're not interested in Swing or Java 2D, I imagine you find my blog pretty dull. If you are interested in Swing and/or Java 2D, you've perhaps been intrigued by the new O'Reilly book "Swing Hacks".

In terms of physical artifact, this is at the better end of O'Reilly's offerings. Decent paper, good image reproduction, and subtle use of the color purple, except in the preface which is made more difficult to read by the absence of anything that ought to have been purple. This book is much better produced than its fatter contemporary, Killer Game Programming in Java. (One final point: the book smells great. I'm not sure if I have a congenital love of this particular smell, or if it's because it's how Acorn's RISC OS 3 Programmers' Reference Manuals smelled. Either way, I like the smell. It's probably killing me.)

I'm wary of being unfair towards this book. A lot hinges on what purpose you think it's meant to serve and, in particular, how you understand "hack".

Amongst people I know, there are three main uses of "hack". It can mean "breaking in to a computer", "coming up with an unorthodox but good solution to a problem", or "coming up with an ugly expedient solution to a problem". Obviously, this book has nothing to do with the first sense (which seems to be being overtaken by the verb "own0r" in my circles, even though the slashdotter's suggested "cracker" never went anywhere amongst people I know).

In this book's title, "hack" seems to mean "ill-advised, badly-implemented, or uninteresting code".

Some hacks are lacking in non-obviousness
A strong candidate for the worst hack in the book is hack #16 "Make Different List Items Look Different". This "hack" is basically just using ListCellRenderer. Seriously. Did they actually mean to call this book "Swing Hacks For People Who Haven't Read Sun's Java Tutorial"?

This hack has other problems, too. It's ugly on-screen, and the source is full of copy and paste code (for which the author actually congratulates himself when he points out that it could have been even worse). Related to the copy and paste code, a more useful hack might have been "You Don't Ever Need GridBagLayout And Should Avoid It", but it looks like someone needs to teach the author that first. He even suggests a visual Swing GUI builder, at which point I heard the sound of foreheads banging against desks.

Some hacks are obvious because they were articles on the Swing Connection years ago. Examples would be the trio right near the start of hack #5 "Add a Watermark to a Text Component", hack #6 "Watermark Your Scroll Panes", and hack #7 "Put a NASA Photo into the Background of a Text Area". (This theme of near-duplicate hacks continues throughout the book. I think they must have been told they had to reach the magic number 100.)

Hack #52 "Use HTML and CSS in Text Components" is truly trivial. This is in the JavaDoc, never mind the tutorial! How can anyone but an absolute beginner (or non-reader) not know that you can do this? It's in the SwingSet demo! The author describes it as a "little-known feature". On what planet?

Hacks such as hack #25 "Export Table Data to an Excel Spreadsheet" are just rewrites of old JavaWorld tips from back when JavaWorld sometimes had interesting content. Hack #81 "Make Mac Applications Behave Normally" is far less detailed than Apple's own "Java Property, VM Option, and Info.plist Key Reference for Mac OS X".

These are the kind of things you get better answers to by just typing your question at Google.

Some hacks aren't good enough to be used in a real program
Hack #17 "Reorder a JList with Drag-and-Drop" is a good example of something potentially useful that just doesn't quite look right.

Hack #42 "Make Your Frame Dissolve", if you run it, is so bad it's almost funny. It would be better described as a failed hack. "Here's something that doesn't really work."

Hack #48 "Make Text Components Searchable" is a really weak implementation, not even bothering to use highlighters. There's a much better example in Kim Topley's "Core Swing: Advanced Programming", which is out of print even though it's the best Swing book I've seen. (Sun's tutorial's pretty good for the beginner.)

Hack #59 "Create a Color Eyedropper" is laughably bad. Run it and see. I cringed.

Some hacks are badly implemented
Even supposing the general idea is sound, some of the implementations are not the kind of thing you'd want your less experienced coworkers copying in to your codebase.

Hack #50 "Auto-Completing Text Fields" creates a Thread rather than a Runnable to pass to SwingUtilities.invokeLater (this is a frequent error in the book; where were the technical reviewers?). And implements ListSelectionListener in a top-level class for no obvious reason. And doesn't bother to pop down the list of completions if focus is lost. And the regular expression in the text is wrong; the one in the code is different but also wrong because metacharacters in the result of JTextField.getText aren't escaped. (The whole use of regular expressions to implement String.startsWith is bad craziness anyway, so even fixing both the regular expressions would still leave me unhappy.) And there's no attempt made to give the pop-up list a pleasing width. If your 12 year old kid had written this, you'd give them a pat on the head for effort, point out their mistakes, and suggest they pair program with you next time. When you see it in a book, it's depressing...

Hack #79 "Launch External Programs on Windows" is about Runtime.exec, which is well-known and well-documented. (See an earlier section of complaints. Many of the hacks actually fit in multiple problem categories.) Also well known, though seemingly not to the author of this "hack", is that you shouldn't use the exec(String) form. Hack #80 "Open Files, Directories, and URLs on Mac OS X" makes the same mistake, but does at least go on to explain how to cope with spaces. If they're aiming at people who don't even know about Runtime.exec, they really ought only show them String[] forms. And would a less valueless "hack" have been to show how to work on MS Windows and Mac OS?

Hack #91 contains ""+(char)0xf099 with no explanation of what's wrong with "\uf099", which seems like it would be strongly advantageous. There's a distressing amount of this kind of voodoo.

There are plenty of unnecessary casts, too.

Some hacks don't work with different LAFs
One of the big problems when customizing Swing components is taking the different LAFs into account. Hack #10 "Building a Drop-Down Menu Button", for example, doesn't work on Mac OS. The author congratulates himself that "one nice thing about assembling the drop-down from standard components is that it will still look good when used with a different Look and Feel", ignoring the fact that you can't change the background color of a button on Mac OS. The screen shot is one of the few that wasn't taken on Mac OS. Presumably someone noticed it didn't work, but they couldn't be bothered to fix it or add a note?

Using setMargin on the JButton used for the down arrow also doesn't work on Mac OS, leaving the button far too narrow for its icon. And the pop-up pops up to the left of the button, not beneath it. (Looking at their screenshot figure 1-28, I'm a little confused as to what they were really trying to achieve. Nothing like any drop-down menu I've ever seen.)

Some hacks are lacking in utility
Hack #51 "Write Backward Text" would be okay if it were the only hack that demonstrates painting into a transformed Graphics2D, but it isn't. So we have a useless effect that we could have inferred an implementation of based on the same technique being used in an earlier hack.

Some hacks are things you just wouldn't want to do
Hack #4 "Display Dates in a Custom Calendar" springs to mind. Date pickers always seem to suck, and this is a particularly weak one.

There's a whole chapter of hacks related to JFileChooser, when the only hack you need there is one that uses the AWT dialogs on Mac OS and MS Windows, and the Swing one on other Unixes (where anything's better than what Motif has to offer). One day, when Sun dumps Motif for GTK+, I'll be able to forget JFileChooser completely. The fact that Sun refuse to fix various problems with the AWT dialogs still isn't enough to make them a worse choice.

There's almost no mention of trade-offs or alternatives
This is one of my most frequent complaints about books, and here it's perhaps unfair. A good example of a book that goes out of its way to present detailed and useful analysis of trade-offs and alternatives is Addison-Wesley's "Design Patterns". As a reference book, it's particularly important that it fulfill that criterion. "Killer Game Programming in Java" was weak for lack of such discussion, I felt, and I expect better from a book that's supposed to be a tutorial. But how about "Swing Hacks"? It's not a reference work, and it's not really a tutorial either. It's more of a bag of ideas.

All the same, I think it would have been a better bag if the authors had explained their reasons for considering and rejecting other implementations. Take hack #15 "Make JLists Checkable", for example. Why doesn't the hack author simply use a JTable with a Boolean column alongside the column representing the list data? It's easy, and it's a lot less weird. This and other hacks smell of hacking for hacking's sake. Hacking in the sense of "ugly solution".

It's a shame to have one of the Swing developers as an author without getting any sort of guidance about "this is the kind of flexibility we were imaging you'd exploit to implement this kind of thing". (I notice that there's little mention of UI delegates in "Swing Hacks". Anyone who's tried to use them knows that, though they must have seemed like a good idea, they're a real pain. If you need to work with UI delegates, it's usually easier to just write your own delegate-less component from scratch. Hack #12 "Add Translucence to Menus" is an exception.)

It not just that, though. Even within the bounds of a particular hack, they don't tend to explore alternatives. Take hack #44 "Turn Dialogs into Frame-Anchored Sheets", for instance. There are two obvious ways to implement a sheet. One is to add a component to the glass pane. The other is to use a WindowListener to make one window follow another about (and stay on top of it). The book only discusses the glass pane method, not even mentioning that on Mac OS it looks totally wrong because the component doesn't have the shadow that a real window does. Use a real window, though, as I've done in SCM, and you find that the disadvantage is potential lag if the use does move a window around while it has a sheet showing, and problems coordinating the window's level in the desktop's z-order (which Java gives you only very primitive access to). Their sheet slides out all wrong, too. I didn't even bother implementing that. Apple keep making it quicker in recognition of the fact that it's not very useful and vaguely annoying.

Summary

Poor (or absent) technical reviewing seems to be a common problem amongst O'Reilly books. It's been a long time since I've seen one I'd unequivocally recommend. If you know about the glass pane, know about java.awt.Robot, know about overriding paintComponent to modify the Graphics2D or to paint into an image, you'll probably find this book boring. If you don't know about these things, but you do know what you're doing to a sufficient extent that you're not scared to look dodgy code in the eyes, and think you can see through some of the implementation details to the ideas, then you might like "Swing Hacks".

I strongly recommend that you download the source from O'Reilly's web site, and check out the overall quality of "hack" before you give them any of your hard-earned cash for the dead tree.

The book's not a total wash-out. I may already have better implementations of some of the hacks, but I'll be reconsidering the trade-offs where "Swing Hacks" offers an alternative implementation, and gratefully accepting a few tricks that I don't already have. My reaction to many of the hacks of "my mum could do better than that!" also means I'll be having fun putting my money where my mouth is.

Appendix

I know you're dying to know which hacks I found interesting. So far I've mainly talked about hacks that I didn't like for one reason or another. So here you go:

  • Hack #8 "Animate Transitions Between Tabs". I don't see the point of animating JTabbedPane transitions: it just seemed to make the UI slower to use. But if I had a similar class (call it JTransitionPanel, say) where transitions weren't initiated by the familiar tab interface, it might be useful. I'm thinking of something like flipping Apple's Dashboard widgets.
  • Hack #35 "Add Windows Resize Icons". Mac OS' grow box looks wrong in Java applications because it doesn't use transparency. Maybe I should be drawing my own?
  • Hack #37 "Save Window Settings". I already have an equivalent implementation of this, but this has reminded me that I should make a greater effort to keep saved state up to date. And a greater effort to code in a style amenable to it.
  • Hack #39 "Spin Open a Detail Pane". A good idea with an okay implementation, but it looks hideous. Spruced up, it might be interesting in places where I currently unconditionally show stack traces and the like.
  • Hack #56 "Create a Magnifying Glass". It's about time I got round to finishing my Java replacement for Apple's Pixie.
  • Hack #96 "Debug Components with a Custom Glass Pane". I once wrote something that aimed to be a Java equivalent of the Self core sampler. This reminds me that I'd like to write that again, and finish it this time.

2005-07-10

If Linux is faster, it's a bug

I haven't used Solaris since the 1990s. I remember having an Ultra10 and being amazed at what a different machine it felt like if I booted Linux/SPARC instead of Solaris/SPARC. (These were the days when "Ultra" meant UltraSPARC, instead of "AMD" as it seems to with the new Ultra20.)

I don't know if Solaris has improved. I was tempted by an Ultra20, but a friend who's heard one on the other side of a closed fire door tells me they're loud. Too loud to share a house with.

All the same, I was pleased to read If Linux is faster, it's a Solaris bug!, and can only hope that Apple takes a similar stance. Mac OS' file system caching sucks compared to Linux. On Linux, if you find(1) and grep(1) on a big source tree, it might take several seconds the first time. The second time around, though, it'll take a tiny fraction of a second. On Mac OS, no matter how many times you do the same search, it stays slow.

Perhaps I'd have to trade off some Mac OS feature to get the Linux performance, but I'd love to know what. (Spotlight may have made things slower, but not significantly, and my complaint isn't the worst-case performance, it's that the average is pretty much equal to the worst-case.)

I'd still probably make the trade on any development machine.

Calculators continue to suck

At some point, Harold Thimbleby seems to have gotten a bee in his bonnet about pocket calculators. He's been complaining about them for years now. And he seems to have infected his son. A friend mentioned their latest work, "Weapons of maths construction" which seems to have been retitled "Calculators of maths construction" since the 2005-07-07 London bombings.

There's an on-line Java demo you can try if you ask Google. If you used to own a Newton, as I did, the whole experience is pretty familiar. Only the handwriting recognition isn't as good (or general) as the Newton's. If you're a Mac OS user, you'll recognize the icons.

I just don't get the point of what they're trying to do. It seems as relevant to 2005 as making a better orrery would be. Even the motivation in their "why" section sounds silly: "A slip or an unnoticed oddity of calculation can cause disaster, from paying the wrong bills, getting the wrong dose of medicine, to throwing an aircraft off course. It is very important for everyone to be able to do mathematics accurately and easily!"

Who does any of these things with a pocket calculator?

What I'd like to see fixed is the kind of "desktop" (in the GUI sense) calculators that ship with operating systems. I'm sick of them, and I'm sick too of the crappy command-line ones with their hobbled languages and dialects. bc(1) and dc(1) can kiss one of my ass cheeks each.

Mathematica, on the other hand, is cool. It takes advantage of the computer, rather than slavishly imitating what we had before. At the same time, it does a good job of producing familiar-looking output.

I can correctly type an expression into Mathematica tens of times faster than I could fiddle with the Thimblebys' awkward "natural" interface. Maybe there is a use for "interactive whiteboards in classrooms", but I thought the idea in class was to ask the students what the answer was? As for pen-based computers, the real fix for those is to give them keyboards, but if you can't do that, I'd still rather give hand-written input to something like Mathematica that isn't so fussy about exactly where I write each symbol, and doesn't waste my time by incorrectly guessing where to insert each symbol.

And I'm supposed to be impressed by e? The Thimblebys' calculator doesn't even draw graphs!

Drawing graphs is something I can see a classroom use for: "So this is what that function looks like, and if we turn this into a parameter, we get this family of curves (these are just the integer values, but here are a few real values too)..."

2005-07-09

Columba

Apple's Mail went downhill in 10.4, on the whole. The searching is improved, but just about everything else is worse. The improvements to NSTextView to support better HTML editing seem to have introduced a lot of redraw and functional bugs. The IMAP handling seems to have become less robust, and Mail seems to have developed the bad habit of lying about what it's done. Sometimes it will look like it's deleted a message, for example, but when you quit (at which point it hangs for minutes) and restart, the messages are back from the dead. Not even marked read. (I thought other mailers were ugly in putting a line through messages pending deletion, but I'd rather have ugly than untrustworthy.)

I've talked before about my problems with Evolution and Thunderbird.

So I'm pretty receptive at the moment to the idea of a Java mailer that I can run anywhere, and that's likely to be more hackable than a single-threaded C monstrosity. I try to steer clear of other people's unfinished software, but "1.0 release candidate 3" sounded close enough to be worth a try.

Columba's IMAP performance is criminally bad. I read somewhere on their web site that there's no caching, and it shows. Even on a LAN, it was annoyingly slow. This could presumably be fixed. (On the plus side, Columba doesn't default to removing messages from the server, something several other mailers have screwed me with in the past, shortly before being uninstalled as retribution.)

Columba offers the full range of reply/forward options, but I was disappointed to see that the default toolbar has "Reply" (which is almost never the right choice) but no "Reply All". (Apple's Mail lacks the ability to forward or reply without mangling the original message. There isn't even "Forward As Attachment", which is the usual work-around for over-zealous wrapping and the like.)

Columba lets you see text/plain and text/html attachments inline, like Evolution. I like this. Mail only shows images inline.

Columba doesn't use the platform's native HTML editor. Its Swing-based HTML editing sucks. Plain text editing is fine, but nothing special.

Columba highlights the current message in black on dark blue on MS Windows. This, as you can imagine, isn't readable. The current mailbox, by contrast, is highlighted sensibly. (This seems to be true on Linux too, judging by the screenshots available on their web site.)

Columba got itself uninstalled when it corrupted its configuration files and refused to start.

When they hit 2.0 it might be worth another look, but right now, they don't even get a link.

2005-07-03

File Alteration Monitoring

Failed detective work

I set out one morning a few weeks ago to find out how Spotlight works. I don't have the answer, but I have some information, and I thought I'd share it.

lsof(1) can show you a process' open file descriptors. Guessing that mds is the Spotlight MetaDataServer, I got this:

hydrogen:~$ sudo lsof | grep mds
mds 21133 root cwd VDIR 14,2 1190 2 /
mds 21133 root txt VREG 14,2 563136 2668589 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Support/mds
mds 21133 root txt VREG 14,2 81316 2665889 /System/Library/CoreServices/CharacterSets/CFUnicodeData-B.mapping
mds 21133 root txt VREG 14,2 17688 2665888 /System/Library/CoreServices/CharacterSets/CFUniCharPropertyDatabase.data
mds 21133 root txt VREG 14,2 352454 2665887 /System/Library/CoreServices/CharacterSets/CFCharacterSetBitmaps.bitmap
mds 21133 root txt VREG 14,2 17852 2937913 /System/Library/Caches/com.apple.IntlDataCache.tecx
mds 21133 root txt VREG 14,2 82136 2682142 /System/Library/CoreServices/Tokenizers/ja.tokenizer/Contents/MacOS/ja
mds 21133 root txt VREG 14,2 9826240 2672194 /usr/share/icu/icudt32b.dat
mds 21133 root txt VREG 14,2 1079968 2671992 /usr/lib/dyld
mds 21133 root txt VREG 14,2 4213200 3497948 /usr/lib/libSystem.B.dylib
mds 21133 root txt VREG 14,2 1227572 3497950 /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
mds 21133 root txt VREG 14,2 1455656 3498058 /usr/lib/libicucore.A.dylib
mds 21133 root txt VREG 14,2 801160 3497957 /usr/lib/libobjc.A.dylib
mds 21133 root txt VREG 14,2 3350216 3497951 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore
mds 21133 root txt VREG 14,2 861592 3498039 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit
mds 21133 root txt VREG 14,2 3182204 3498002 /System/Library/Frameworks/Security.framework/Versions/A/Security
mds 21133 root txt VREG 14,2 252180 3497963 /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration
mds 21133 root txt VREG 14,2 3744936 3497959 /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
mds 21133 root txt VREG 14,2 203956 3497966 /System/Library/Frameworks/DirectoryService.framework/Versions/A/DirectoryService
mds 21133 root txt VREG 14,2 103664 3498082 /System/Library/PrivateFrameworks/DSObjCWrappers.framework/Versions/A/DSObjCWrappers
mds 21133 root 0r VCHR 3,2 0t0 43420804 /dev/null
mds 21133 root 1w VCHR 3,2 0t700 43420804 /dev/null
mds 21133 root 2r PSXSHM 0x041c5824 4096 obj=0x02d0e270
mds 21133 root 3r PSXSHM 0x02bd6c04 4096 obj=0x02c6a658
mds 21133 root 4u VREG 14,2 194596864 3262066 /.Spotlight-V100/store.db
mds 21133 root 5u VREG 14,2 194596864 3262067 /.Spotlight-V100/.store.db
mds 21133 root 6u VREG 14,2 0 3262068 /.Spotlight-V100/.journalHistoryLog
mds 21133 root 7u VREG 14,6 151552 6930 /Volumes/Elliott Hughes’s iPod/.Spotlight-V100/store.db
mds 21133 root 8u 0x029b4980 file struct, ty=0x7, op=0x369370
mds 21133 root 9u VREG 14,6 151552 6931 /Volumes/Elliott Hughes’s iPod/.Spotlight-V100/.store.db
mds 21133 root 10u VREG 14,6 0 6933 /Volumes/Elliott Hughes’s iPod/.Spotlight-V100/.journalHistoryLog
mds 21133 root 11r VREG 14,2 936 3498501 /private/var/run/utmp
mds 21133 root 12u KQUEUE 0x029b8ec0 count=0, state=0x2
mds 21133 root 13u IPv4 0x0439cf58 0t0 TCP localhost:cisco-tdp->localhost:netinfo-local (ESTABLISHED)
mds 21133 root 14u VREG 14,2 150994944 3262069 /.Spotlight-V100/ContentIndex.db
mds 21133 root 15u VREG 14,6 6144 6949 /Volumes/Elliott Hughes’s iPod/.Spotlight-V100/ContentIndex.db
hydrogen:~$

Which looks plausible. It's got the right files open. Notice that lsof(1) knows relatively little about fd 8. If we point fs_usage(1) at mds and modify a file, we see something like this:

12:39:13.450 read F=8 B=0x4c 0.005875 W mds

That F=8? That's the bad news I was half expecting to hear. mds gets its notifications via this mystery kind of file descriptor.

A tour of file alteration monitoring techniques

You may have heard around the time that 10.4 was released that Spotlight used BSD's kqueue notification mechanism. Apple posted sample code called filesystem_examples during WWDC 2005, and one of the examples, kqueue_fragment.c shows you how to use kqueue(2).

The trouble with this is that you have to give a file descriptor to monitor vnode events on. And we want to monitor an entire file system (or a significant subtree of it). There's no constant in sys/event.h to let you monitor all files, and trying the obvious values of 0 and -1 doesn't work. (0 unsurprisingly, because it's a perfectly valid file descriptor that a process might have open. Given the number of processors where 0 sometimes means the literal and sometimes means the register, it seemed worth a try.) And running lsof(1) on Apple's sample doesn't show anything like the mysterious file descriptor 8 of mds, so it really does look like this isn't the mechanism.

The kernel event queue mechanism, then, looks rather like Linux's F_NOTIFY (see fcntl(2) for details), which shares the limitation of requiring you to register an interest in all the files and directories you find, and taking special note of any new files created. The BSD mechanism simply trades off inventing a new mechanism for kernel/process communication against the ugliness of re-using signals (which is what Linux does). Linux 2.6 offers inotify, which at least addresses the problems of using too many file descriptors and using signals as a communication mechanism.

As usual, still other Unixes have their own mechanisms. SGI had their own imon(7) inode monitor device, but they also had the good sense to write fam(1), the File Alteration Monitor. This offers a portable interface to the file alteration monitoring facilities of any Unix. The implementation will use whatever the system's best alternative is, even falling back to polling on a system that's too primitive to support anything better. (The potential advantage being that if multiple clients care about the same file, only one process need poll it. Plus you can write your application one way and know that it'll work as well as possible on any given Unix.)

fam(1) is really clever in that if it's asked to monitor a file on an NFS mount, it will try to contact the daemon on the NFS server (and fall back to polling remote files otherwise). Again, this is transparent to your application.

Debian Linux seems to ship with fam(1), seemingly because GNOME uses it. Mac OS doesn't. I don't know about Solaris.

There are two problems, though, above and beyond the question of whether it's running on your system. The first problem is the usual one of only working on individual files and directories, and not directory trees. The second is that there's a limit of 1000 monitoring requests per process. So if you need to monitor a tree of 25,000 files, you're going to need some extra cunning. There are also security concerns associated with running the daemon as root.

There's an inotify-based rewrite in the works, but it's not obvious why that's a better idea than patching FAM. At least it uses an extended subset of the same API, so if you stick to the intersection, your application should just work. The rewrite's lack of support for NFS seems like a step backwards.

MS Windows has FindFirstChangeNotification, which takes a boolean that specifies whether to monitor the given directory or to include its subdirectories too. Unfortunately, as far as I know, this doesn't work on remote file systems, so although it's the interface I want, it doesn't necessarily have the implementation to back it up. And, of course, it's specific to MS operating systems which – though I try to support them – I don't actually use.

The Unix API is fine if your application is to monitor a configuration file, say, and automatically re-read it if it changes. But it sucks if you're an editor, say, and you want to automatically respond to changes to files. Perhaps the user's version control system has just added or removed a few files, and you want to update your index. Or a file has changed outside of the editor for whatever reason, and you want to update your live search results. These things are hard to do, and that's a pity, because they're exactly the things I want to do. (At the moment, my editor knows when it has modified a file and will update its search result accordingly, but it has no clue about external modifications.) Before Spotlight, virus scanning was perhaps the best-known application for the style of notification that includes subtrees. This could be why MS Windows has the best support for it.

If you're unfamiliar with file system or protocol implementation, you might be wondering why this is all so challenging? Surely you just need to do a few string comparisons? The problems include the fact that most implementations won't actually have the pathname conveniently available (the notion of "file handle" goes all the way down, and isn't just a wrapper for the name), the existence of links (both hard and symbolic), and the security concerns about giving away information without going through the usual open(2) path. That's one reason why it's so common to require a file descriptor: it acts as proof that you're allowed to know something about the file or directory in question.

A system that might become important in future is Dazuko. (The name is Germanic, in the style of Flak or Kripo.) It offers a very nice API, but at the time of writing it supports neither Mac OS nor MS Windows, and lacks the ubiquity that would make it really useful. Because of its intended security applications, Dazuko even lets you veto file accesses.

For now, though, I guess I'm stuck. None of this is really suitable for my application, and I can't even knock up a quick hack for the benefit of Mac OS users.

2005-07-02

Joplin

I've been saying for years that Sun needs to put some money and people into writing a real Java desktop (as opposed to just giving GNOME a Java-related background image). The main reasons being that it would be useful, be a good demonstration of faith in Java, and perhaps make them finally realize that you can't develop a good library without writing serious applications and letting that experience feed back into your library development.

So I was interested to try Joplin, an iTunes-like music player. Here's how to build and run it on Mac OS (which doesn't include ant):

# cvs co ... joplin
cd joplin/src/java
JOPLIN_JARS=`ruby -e 'puts(Dir.glob("../../lib/*.jar").join(":"))'`
find . -name *.java | xargs javac -cp $JOPLIN_JARS
java -cp $JOPLIN_JARS:. joplin.Tunes4

If you try this, you'll see that it deadlocks because they haven't followed their own advice about constructing the GUI on the AWT thread. (They do in Tunes2, which otherwise looks worse.) You'll also want a patch like this so that you don't have to fight the hideous JFileChooser, and so that any of tracks are recognized. (Does the Win32 version of iTunes really append "/" to each filename?)

--- joplin/dataset/ITunesDataProvider.java 20 Jun 2005 21:34:32 -0000 1.5
+++ joplin/dataset/ITunesDataProvider.java 2 Jul 2005 18:12:05 -0000
@@ -49,7 +49,6 @@
public ITunesDataProvider() {
// this("c:\\Documents and Settings\\Richard\\My Documents\\My Music\\iTunes\\iTunes Music Library.xml");
if (libraryPath == null) {
- //TODO automatically look for the file in the default Windows location, and in the default Mac location
libraryPath = this.getDefaultPath();
if(libraryPath == null) {
//before asking
@@ -68,6 +67,10 @@
if(lib.exists()) {
return lib.getCanonicalPath();
}
+ lib = new File(home,"Music/iTunes/iTunes Music Library.xml");
+ if(lib.exists()) {
+ return lib.getCanonicalPath();
+ }
} catch (IOException ex) {
System.out.println("couldn't find the itunes dir directory.");
return null;
@@ -330,7 +333,7 @@

public Track(Map track) {
try {
- if (!((String)track.get("Location")).endsWith(".mp3/")) {
+ if (!((String)track.get("Location")).endsWith(".mp3")) {
return;
}

It's pretty slow to start up, for reasons I can't be bothered to investigate. It doesn't seem to recognize my album art (though it did for one CD, once, and then suffered from the common Mac OS problem of BufferedImage channels being mixed up). The player's fine, though, and hardly uses any CPU. The visualizer (which I'd never use anyway) looks fine to my uncaring eyes, but it's a huge CPU hog.

The game-like cycling through the CD box images is okay, but resource-intensive, and because of that, it gets really jerky. It looks like it might be pretty neat, though, if there were one CD "avatar" per album, rather than per track, which is just silly. I have 5,000 tracks, which just isn't navigable by those means. (This is also why, when people call me an Apple fan-boy and suggest that there are good alternatives to the iPod, I just laugh. Less than 30 GiB? Here's a nickel, kid: buy yourself a proper mp3 player!)

There's definite promise, but it hasn't reached the critical mass where I'd really be interested in working on it and contributing patches myself. (Am I the only one who finds java.net really awkward? Why can't I submit a patch or post on a discussion forum without "requesting a project role"? Why add this artificial barrier to entry? Yes, you'd like people to make a commitment to your project, but if they don't feel prepared to do so, why wouldn't you still want their one-off patches?)

2005-07-01

Book: "Killer Game Programming in Java"

Before we get into this, I should should say that I have no interest in games. I don't play games, I don't write games, and I don't want to write games. I am, however, interested in high-performance graphics, and am potentially interested in non-game applications of 3D graphics.

So that's how I came to be reading a book on games programming (with an embarrassing title).

Basics
Chapter 2, "An Animation Framework" introduces double-buffering, and shows how to implement it in a JPanel subclass. There's no mention of the fact that Swing provides double-buffering way up in JComponent. A few pages later, the author switches from AWT-like repaint-driven drawing to what the author calls "active rendering": calling the painting code directly from the game's main loop. So if you wait long enough, reimplementing double-buffering makes more sense, but in the meantime you're left with a feeling of distrust in the author's skill.

And even when you've waited, you're left wondering if the author knows about JComponent.paintImmediately, and whether he has some reason to avoid Swing's seemingly far more sophisticated double-buffering. It could be that there is a good reason. JComponent.paintDoubleBuffered and JComponent.paintWithOffscreenBuffer are private, so you'd have to accept all the complexity in JComponent._paintImmediately for dealing with overlapping components. Maybe for games that outweighs the benefits of the more sophisticated double-buffering.

But that's surely a decision worth talking about? If I'd made the decision in some of my code, I'd definitely write a comment. When we're talking about a book, where the programs aren't ends in themselves but examples used to demonstrate and explain techniques, it would seem essential to explain why you aren't didn't make the most obvious choice.

Conversely, coverage of the options for timers and reasons for choosing between them is pretty strong.

Imaging
Chapters 5 and 6, "An Introduction to Java Imaging" and "Image Loading, Visual Effects and Animation", are pretty good. The former is a whistle-stop tour of various alternatives (and, if you know Java, you'll know there are plenty of alternatives) with some discussion of which you'd choose when. The latter is an equally quick tour of some of the options for rendering and processing images once you have them.

Sound
I flipped through the chapters on sound, because I've never had any need for it, nor can I imagine myself doing so. I don't write games, and in real programs, use of sound tends to be a mistake because the user's actually listening to a Haydn string quartet with the volume up really loud so they can hear the subtle articulation... BEEEEP! Well done: you just gave your user a heart attack, and now their monkey brain is panicked, and it's going to take a while for them to calm back down and work out what just happened.

2D games
Chapter 11, "Sprites", describes a simple game, similar to Arkanoid, but without the bricks. The image used for the bat is of a cartoon ant. This not only makes the use of the word "bat" confusing because the image isn't of a table-tennis bat; it also introduces ambiguity with "bat" the animal, because "ant" brings animals to mind. It's almost funny to see the whole chapter stumble over itself, including explanatory notes in several places intended to help the reader cope with a completely self-inflicted wound.

In the same chapter, there's this code:

player = null; // for garbage collection of previous player
player = new ImagesPlayer(...

I find this distressing from an educational point of view. There are cases where the "x = null" idiom can be useful, such as when x is an array element (see the implementation of ArrayList for an example), or when you're finished with x but about to do a lot of work before you next assign to x (this might be true here, for example, depending on the constructor for ImagesPlayer). But there's no mention of this at all in the text.

If it's non-obvious enough to be worth a comment, it's non-obvious enough to be worth a good comment.

Chapters 12 and 13 conclude the 2D part of the book. (Yeah, it's a pop-up book from then on. You're so funny.)

If you hadn't already noticed that the book is let down by the poor quality of image reproduction, you really notice around this point — the point where actual games are introduced. Not only is there no color, the images have either been crudely scaled or overly-compressed using some lossy mechanism. It's really hard to see anything. The line drawings are fine; it's the screenshots that are unrecognizable.

3D games / Networking
I stopped reading at this point. I'll come back if and when I have reason to, but as I said at the outset, my interest is really in 2D rendering.

Summary
The book suffers from not providing sufficient rationale for design choices. Worse, there's little or no attempt to write reusable classes (or explain why that's impossible or undesirable). The authors idea of reuse appears to be of the copy & paste variety, where the copies get new bodies for private methods such as gameUpdate and gameRender, and where you write new boilerplate code for key handling in each game rather than knocking up any kind of system for declaring the keys you're interested in. (There's the occasional protected method, so it's not like the author's a total C programmer.)

On a related note, the book suffers from what feels to me like an insufficiently clear distinction between general principles and the specifics of implementing the particular game/demo each chapter sets out to build. So not only is it not obvious what code you're supposed to take away as generally useful, it's also not obvious how general-purpose the various ideas are, nor usually what the alternatives are, and how you'd choose between them.

And, as I've said, the quality of image reproduction is truly abysmal.

You're too... Orange!

I remember one spring in the late 90s being distressed to see the populace start to swathe themselves in safety orange. A color previously reserved for those doing dangerous work, and rightly so. As is the nature of fashion, it was gone by winter. But there was still too much orange in the meantime.

Fashionista that I am, I wore the same clothes as I'd worn the year before, and continued to wear them until they wore right out.

And here it comes again. Orange, that is. First Apple with their WWDC 2005 t-shirts, then Sun with JavaOne 2005 (this link looks like it will be reused in 2006, but I couldn't find anything more permanent), and now there's JBox Embedded J2SE Platform.

Despite the offensively garish color scheme, I'd be interested in one of those if I weren't aware of the industry's tendency to mean "low performance" when they talk about "embedded" parts. Yeah, they'll guarantee availability for 5 or 10 years, but the part's an underachiever today, never mind 10 years into the future. Which is okay for a toaster, but worrying for a development machine. (Strictly, they talk about it as being for deployment, but who would deploy a bright orange box?)

Anyway, here are my fashion predictions for next year... Google image search tells me that tigers are orange, from which I deduce that Apple will be sporting yellow next year, since leopards would seem to be yellow (or white, but there are only a few thousand of those, and we could probably wipe them out in the meantime if we only put our minds to it).

Sun will probably be going for metallic red, because that's the color of mustangs, except when they're metallic blue. (No links to car sites; I'm too much of a human-power/public-transport pinko for that kind of thing.)