2005-09-25

Final verdict on Apple's "Mighty" Mouse

I mentioned it already, but I get the impression I haven't mentioned it forcefully enough: Apple's new mouse sucks. In a bad way.

The scroll nipple would be great, on a better mouse. And Apple's new mouse looks better than most, if you can ignore the blatant mistake that is being a multi-button mouse that looks like a single-button mouse. But in use, it's rubbish. You can call me stupid for not being able to learn to lift the left side of my hand sufficiently to use the right-hand button without the mouse interpreting it wrongly, but you'd be wrong. A mouse that requires me to make allowances for its physical and mental defects is just wrong.

When he's finished with "clever" doors and faucets, maybe Donald Norman can write a book about "clever" mice.

The "Mighty" Mouse is a triumph not even equal to the recent Down's syndrome recreated in mice story. I even wonder if there's a connection. It's certainly spooky that two groups of people would go to such lengths to deliberately create deficient mice.

At least Apple's mouse doesn't raise many ethical questions.

Java source differences between Unix and Win32

I explained in a recent post that java.io.FileDescriptor on Win32 has different Java source to the same class on Unix. Despite the fact that it's a public class. I didn't know this was allowed, and was interested to find out where else it occurs.

To this end, I decompressed src.zip from Java 1.5 on both Win32 and Linux, and did a recursive diff(1) of the two trees. Here is a complete list of the differences:

  • First up is java.io.FileDescriptor, which I talked about already.

  • There are the package-private classes UnixFileSystem, Win32FileSystem, and WinNTFileSystem, all implementations of the package-private FileSystem abstract class. This seems fine.

  • There are various java.lang.CharacterDataX classes, generated by scripts during the build process. They contain comments showing that the builds were done at different times, and that the command-line arguments differed, but the classes themselves are identical. And anyway, these too are package-private.

  • java.lang.ProcessEnvironment and java.lang.ProcessImpl differ, but again they're package-private classes. I'm not sure why they don't follow the UnixProcessEnvironment/Win32ProcessEnvironment pattern, though.

  • java.lang.Terminator (again package-private) doesn't register a signal handler for SIGHUP on Win32. This is implemented by copying the class and removing a line rather than by run-time test.

  • java.lang.UnixProcess isn't included in the Win32 src.zip.

  • java.util.prefs contains different package-private classes on Unix and Win32.


It's not Java source, but there's also the launcher's java_md.c and java_md.h files ("_md" being Sun's convention for naming files containing machine-dependent source). And that's it.

So it's only java.io.FileDescriptor that seems dodgy. That's a relief.

2005-09-24

requestFocus, requestFocusInWindow, toFront, Mac OS

You may be more on the ball than me, but it's taken me until very recently to understand the difference between Java's requestFocus and requestFocusInWindow methods. They draw a distinction between giving the focus to a component even if that requires changing the currently focused window (requestFocus) and giving the focus to a component only if its window has the focus (requestFocusInWindow).

So if you've got a Java application that's being annoying and stealing the focus from other applications, it's likely calling requestFocus when it should be calling requestFocusInWindow.

(This problem can be more difficult to spot than you might realize. If you have code that does some work and then requests focus, you can miss this problem if you're the sort of person who always waits for it to complete anyway, or if it usually completes so fast you can't get bored and start doing something else, or – as I'll mention later – if you're on Mac OS.)

Requesting focus is made slightly more confusing and complicated than described above by platform differences.

When I'm using Linux, a new window will automatically get the focus. When I'm using Solaris, with what seems like the same window manager (but presumably with some different configuration option somewhere), a new window won't get the focus. Should my application gain a call to requestFocus? On the one hand, it would make the application far more usable (it's crazy for the user to ask for a dialog to appear but not have the focus go to the new dialog), but on the other hand it seems to be deliberately going against some aspect of the user's configuration. On the other other hand, that user may, like me, not have explicitly chosen such a configuration, and may, like me, not have a clue of how to fix it. (I'm not actually certain it's quite as simple as a matter of configuration, because sometimes it works as I'd expect.)

When I'm using Mac OS, there's yet another notion of focus: application focus. So requestFocus behaves slightly differently again. It will transfer the focus to a window only if the application is currently focused. (Thanks to the screen menu bar, an application can be focused even when it has no windows open. Despite having used Mac OS since 2001-12, I still find it odd that I can close every Safari window, say, and have iTunes as the only window on the display, yet Safari still has the "focus". It's especially disconcerting if you leave the computer and come back to it in such a state.) You might think toFront would be the answer, but it isn't.

If you ask on Apple's java-dev mailing list, as I did, how to get around this restriction, you get two kinds of response. There's the "you're evil, Mac applications shouldn't do that" response and the "you can't" response. (Strictly, there was a third, suggesting I use open(1), but I wanted something that worked for arbitrary Java programs, even ones not packaged as a .app/ directory.) Neither of these two claims are true. If you have two applications working in concert, as in my case, it's idiotic for them not to be able to transfer focus between themselves. And it turns out you can give the focus to an arbitrary program, if you're prepared to write a bit of C++.

Here's the version of the code in salma-hayek at the time of writing:

#include <Carbon/Carbon.h>
#include <iostream>

/*
* On Mac OS, there's a strong notion of application. One consequence is that
* toFront doesn't work on an application that isn't the one the user's
* interacting with. Normally this is fine, if not preferable, but it's awkward
* for a Java program that wants to be brought to the front via indirect user
* request. For example, without this, it's impossible for the "edit" script
* to bring Edit to the front on Mac OS.
*
* (This file is Objective C++ simply so that the make rules add
* "-framework Cocoa", which will drag in Carbon for us.)
*/

static void bringProcessToFront(pid_t pid) {
ProcessSerialNumber psn;
OSStatus status = GetProcessForPID(pid, &psn);
if (status == noErr) {
status = SetFrontProcess(&psn);
}
if (status != noErr) {
std::cout << "pid " << pid << " = " << status << std::endl;
}
}

int main(int, char*[]) {
bringProcessToFront(getppid());
return 0;
}

Spawn this from the Java program that wants to grab the application focus, and hey presto! You're the focused application. My editor uses this to come to the front when the user causes it to open a file from a terminal emulator.

2005-09-19

Pick Yourself Up

You know how in movies like "Solaris" or "Eternal Sunshine of the Spotless Mind" there are moments where some otherwise innocent thing is a clue to another reality leaking through? The kind of thing common in Philip K Dick books? I was on the light rail yesterday and amused to hear a young person's phone play something I haven't heard in years. So long, in fact, that it took me a while to recognize it.

As I stepped off at my stop, though, the words came to me, and I realized it was Pick Yourself Up from the Fred Astaire/Ginger Rogers 1936 musical "Swing Time". A few days prior, I'd thought it odd enough to hear a modern version of Phil Collins' "Easy Lover" in a club. Not realizing at the time how apposite it would come to seem.

Anyway, I think all this is another clue that none of this is real. This whole soundtrack to my life is just there for the audience's benefit.

I just wish my character were more popular with the focus groups.

2005-09-18

Sun Ultra 20

Every few years, it seems, Sun makes a noise about their new cheap workstation for developers. Until the Ultra 20, their idea of cheap and mine have been rather different. The Ultra 20, though, was interesting for a variety of reasons, in no particular order:

  • I've liked Sun's hardware for as long as I can remember. From before I went to university, Suns represented real computers. Even if I'd seen an equal amount of SGI hardware in the flesh, and SGI's hardware of the time was more impressive, I think it counted for a lot that I'd seen Sun hardware first.

  • I've always liked Solaris. I haven't used it for years now, but until GNU/Linux, Solaris was the only Unix I'd used that felt right. This can't be accounted for by primacy: I'd used AIX, IRIX, and SunOS 4 quite extensively before I came to Solaris.

  • Sun's making weekly builds of Java 6 available, and Apple still hasn't finished porting Java 5. This is a constant source of aggravation to me, and it's entirely elective.

  • dtrace(1) looks interesting and useful.

  • I may have thought I'd seen the last of the Z80, but now even Apple is giving in to the Intel hegemony, I'm going to have to learn x86 one of these days. A spiteful streak suggests that it may as well be on amd64.

  • Mac OS 10.4 just isn't as good as 10.3 was. I don't know if this is just a temporary slip-up, or if this is pre-NeXT Apple starting to outweigh the infusion of sense and taste that NeXT represented. So I'm starting to consider my exit strategy. Firefox is as good as Safari.app, Mail.app's getting worse rather than better, Terminator is as good as Terminal.app except on Mac OS, and most of the other applications I use are Java applications. The only things I know won't be easy to replace are Dictionary's New Oxford American Dictionary and Preview's handling of PDF (and even there, GNOME's evince is a significant improvement on other non-Apple PDF viewers).


So after much milling about, moving of house, and increasing of incredibly low US credit card limit, I bought an Ultra 20.

There were a few aspects of the buying process I didn't like. For one thing, the "reviews" link didn't actually have any reviews of the Ultra 20. Why draw attention to the fact that pretty much no-one cares about your latest machine? I also didn't think the technical specifications were as good as you get on Apple's site. On Apple's site it's pretty easy to see, for example, what difference the various graphics cards make. After much surfing of the web, at the time I ordered I had no idea whether my Ultra 20 would be able to make good use of a 23" Apple display. (On the other hand, Apple's specifications are full of irrelevancies such as "Simultaneous issue of up to 10 out-of-order operations" and "Advanced three-stage branch-prediction logic".)

The worst thing about Sun's store, though, was that there was no indication of availability. Just a link saying "Call Sun". So why would I choose anything but 3-5 day shipping, if for all I know it's going to be 8 weeks before the thing's even ready to ship? (If, on the other hand, it will be ready to ship in 24 hours, I'd prefer next-day delivery. So I guess I set myself up to be annoyed one way or the other) There didn't even seem to be any way to see how much the various options cost, so I just left it on the default 3-5 day and clicked "Continue".

(Sun's web site has these stupid questions at the bottom of each page trying to get some feedback on their site, but not seeming to realize that you don't want to interrupt what you're doing to answer. I would have answered a quick questionnaire at the end, but instead I started writing this.)

Anyway, without Sun ever mailing me to tell me anything had changed, a big box appeared two weeks later with a tiny little box parasitically attached to its side. The little box contained a US power cord. The big box contained an Ultra 20. One demonstration that I'm not as strong as I'd like to be later, as I carried the 20 kg box from work to the nearest light rail station and from a downtown light rail station to home, and I had my first reason to use the very sharp cleaver a friend gave me when I moved in. The same person was scared of a blunt martial arts demonstration weapon I have, but thought nothing of handing me an evil little device that would kill you as soon as look at you.

Hardware set-up
My original plan was to use my 23" Apple Cinema Display, and then either switch to using the Ultra 20 headless, or get another display so that the PowerMac and Ultra have one each. It wasn't going to be that easy. It doesn't appear that the NVS280 card in my Ultra can drive the 23" display. And when you boot, it's in some weird DOS-like text mode. I know this because I borrowed a CRT from a friend in the next block. Damn near dropped the thing, too. Not only could I usefully be stronger, I could usefully dry my hands after tae kwon do class to make them less slippery. The first thing I tried was connecting to the on-board graphics' VGA connector, on the assumption that we'd be in some kind of text-mode installation process that wouldn't be able to drive the NVS280. No output. I vaguely remembered something from what little I know of PC hardware about graphics cards having boot ROMs, and wondered if the NVS280 was announcing its presence and disabling the on-board graphics. The trouble being that the NVS280 only has dual DVI outputs. Luckily, I had a DVI-to-VGA converter lying around from my unused Mac mini, so the Ultra's currently connected to the CRT like that. So beware if you don't have these bits of kit lying around.

Mouse and keyboard, both USB, were trivial. The Ultra's X11 server seems happy with keyboards and mice being disconnected and reconnected, though the console got upset when I booted without a keyboard and later attached one. With an Apple keyboard, the kernel always complains "WARNING: No usable keyboard alias!" on boot. No idea why.

The mouse that comes with the Ultra is nice enough. It feels cheap, but it works much better than Apple's latest effort, and it's not too ugly, and it doesn't have any writing on it (something which really puts me off most mice; I always resent advertising after purchase).

The Ultra 20 is actually more attractive in the flesh than the pictures on Sun's web site would have you believe. The joins between the various panels are poor, especially when seen next to a PowerMac G5 with its near-perfect joins. And I think the front of the case should be larger than, not slightly smaller than, the rest of the case. That and the bevel leave a vague impression of prepuce. And the green power LED is way too bright. It lights up the whole room at night, projecting on to the facing wall. First time I have the case open, I may take the opportunity to disconnect that.

Solaris installation
There's no Solaris CD, but it's already installed and just wants to run through its text-mode configuration program. It's okay. Not as good as Mac OS, but better than Debian. It didn't ask me any questions I didn't even understand (as Debian often does), and it worked most things out for itself. I stupidly misinterpreted its question about DNS as asking whether I wanted to use a DNS server rather than whether this machine would have an entry in DNS. Fixing this was a bit awkward because I had to fill in bogus information until I got to a stage that let me confirm or go back.

You're not asked to create a user. Presumably they assume that people don't use Solaris at home and will be using NIS or LDAP. So I was left logging in as root and subjected to the extreme slowness (and non-obvious usefulness) of smc(1). I'm not sure how that was supposed to be any better than using the command line. I ended up manually editing /etc/passwd anyway so I could have bash(1) rather than sh(1) and because there seemed on next reboot to have been some confusion between /export/home/elliotth and /home/elliotth.

I also wasn't asked for a machine name. I assumed that hostname(1) was what I wanted, but it isn't. As explained here, I needed to echo the name to /etc/nodename to change my machine's name from "unknown". Again, I guess they don't expect many home users.

I might have to give in and learn about setting up DNS. Turns out I can't reserve IP addresses for MAC addresses on my NETGEAR's wireless interface, just on its wired ports. So while I can get by with /etc/hosts entries for the Ultra (which is wired), it's no good for my other machines (all of which are wireless).

Solaris 10
The first command I run on new hardware tends to be ping(1). Solaris' traditionally terse "www.google.com is alive" made me laugh. "Ah, Solaris. We meet again." They should fix that, though.

They should also use up-to-date GNU versions of find(1), tar(1), and make(1) [GNU make is called gmake on Solaris, and is even more hidden than make itself], and include Firefox, Ruby, and Subversion.

Sun's make(1) still lives in /usr/ccs/bin, but since the last time I used Solaris (Solaris 2.6, I believe), /usr/sfw/bin has appeared. So although the default path makes it look like you've got no C++ compiler, fear not: GCC 3.4 is hiding away somewhere. There's Mozilla in there, but no Firefox. Python, but no Ruby. gnome-stones but no Subversion. This is, after all, a developer workstation.

Luckily, it's a pretty fast build machine. And one thing that really stands out is the I/O performance. Maybe not so much if you're used to Linux/IA-32, but if you're used to Mac OS/PowerPC, it's almost shocking. (I'm not just talking about the PowerBooks with their pathetic buses, either; my dual G5 sucks at I/O too.) I bought the middle of the three models, and it's currently my fastest build machine at home or work when it comes to building my various Java projects.

Despite Jobs' claim that Apple would have the best Java anywhere, this is a cheaper machine than my PowerMac, and it builds faster, runs faster, has a much more recent version of Java, and can run the weekly builds of the not-yet-released version. Apple's Java's performance and correctness is very mixed, and there's no sign of a proper release of 1.5.0 even now. (And Mac OS 10.4 is rather ropey. I've been reading my mail via a webmail system all day because Mail.app keeps crashing when I'm replying, without even bringing up crash reporter. Fuck that. Apple may have got a lot of things right that no-one else has, but that's not a license to go round fucking up other things.)

Rumors of the Ultra 20's extreme noisiness were unfounded. I can't hear it over the American refrigerator even when it's building. It makes quite a racket as it powers up, before the software can get the fans under control, but no worse than my PowerMac.

The Java Desktop System, while it still offends me that it contains so little Java, is better than anything Debian's ever managed to offer me on Linux. This is probably what GNOME is supposed to look like. The window manager is perhaps the ugliest I've seen since twm(1), but it's nice to have menus that aren't full of crappy old Athena programs that no-one uses and six different web browsers, three of which are the GUI equivalents of broken links, et cetera. I guess I now need to learn how to integrate my Java programs with GNOME.

If you chose a UTF-8 locale when installing, as any right-thinking person would, you'll be offended by an ugly "language input window" attached to the bottom of the focused window. It usually says "English/European", but actually turns into a menu if you press on it, and lets you choose a different input method. Anyway, here's how to turn it off, though not for Java applications, annoyingly enough. I don't know why I don't see this on Linux, where I also use a UTF-8 locale.

Terminator needed a bit of hackery, both to fix the make rules so that we can build JNI libraries on Solaris with GCC, and so that we get Solaris' unusual STREAMS-based terminal handling to behave like everything else. Everything else was fine, as you'd expect from Java.

apt-get
My friend the Solaris sys admin suggested blastwave as a suitable replacement for Debian's apt-get. I'd found where Sun had hidden GCC, and I'd already built my own make, Ruby, and Subversion, but I balked at building my own Firefox. I followed the instructions linked to in this paragraph, and installed firefox, top, and ethereal. I wasn't tempted by their packages of GNU utilities because they prefix the binary names with 'g', as in "gtar", "gdiff", et cetera. Manually renaming them would seem to defeat some of the purpose.

I initially missed the blastwave equivalent of apt-get upgrade, the unsurprising pkg-get upgrade. Thanks to Joseph Ottinger for setting me right, and for not rubbing my nose in my inability to read documentation, guess, or try --help. Given that, I've dumped my home-made Subversion for the blastwave package. The blastwave Ruby package seems to think it's build with a libruby.so which isn't supplied, so I've gone back to my home-made copy (which doesn't have a libruby.so either, but doesn't think it needs one). I've reported that to the package maintainer. I'm also still using a home-made make(1) because I need 3.81, which is still in beta.

My friend the Solaris sys admin told me when I was having trouble backing out of the blastwave ruby package that the equivalent of apt-get remove is Sun's pkgrm(1), which means you need to have paid attention to the underlying "PKGname" rather than the package name used on the pkg-get(1) command line.

sudo
I haven't spent this much time logged in as root since 1995. Not having sudo(1) isn't quite as criminal as it would be to not have ssh(1), but it's still strange and unpleasant. blastwave offers a sudo package, but I haven't tried it yet.

Anyway, a day's work and I have a usable machine. And as far as I can tell, a pretty good machine. I think the main thing standing in the way of it becoming my main machine at home is its inability to drive a big display.

2005-09-11

Copying images to the clipboard with Java

I wanted to be able to copy an image to the clipboard in FatBits, my Java replacement for xmag(1) or Pixie.app. I was surprised to see the JDK doesn't have an Image equivalent of StringSelection, so I knocked one up:

/**
* A Transferable able to transfer an AWT Image.
* Similar to the JDK StringSelection class.
*/
public class ImageSelection implements Transferable {
private Image image;

public static void copyImageToClipboard(Image image) {
ImageSelection imageSelection = new ImageSelection(image);
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.getSystemClipboard().setContents(imageSelection, null);
}

public ImageSelection(Image image) {
this.image = image;
}

public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (flavor.equals(DataFlavor.imageFlavor) == false) {
throw new UnsupportedFlavorException(flavor);
}
return image;
}

public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(DataFlavor.imageFlavor);
}

public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] {
DataFlavor.imageFlavor
};
}
}

I also raised a bug with Sun, and had it closed as a duplicate shortly afterwards.

Anyway, imagine my disappointment when Apple's JVM just hangs:

2005-09-10T10:02:55.916-0700 unknown: (hang #1) event dispatch thread stuck processing event for 1099 ms:
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:474)
sun.awt.image.ImageRepresentation.reconstruct(ImageRepresentation.java:103)
apple.awt.CDataTransferer.imageToPlatformBytes(CDataTransferer.java:238)
sun.awt.datatransfer.DataTransferer.translateTransferable(DataTransferer.java:1180)
apple.awt.CDataTransferer.translateTransferable(CDataTransferer.java:126)
apple.awt.CClipboard.setContentsNative(CClipboard.java:67)
sun.awt.datatransfer.SunClipboard.setContents(SunClipboard.java:93)
e.util.ImageSelection.copyImageToClipboard(ImageSelection.java:16)

The hang lasts indefinitely. The 1099 ms is just how long it takes for my EDT hang monitor to notice something's up. (If you have it run much quicker than that, it's constantly reporting slow font rendering and the like, especially on Linux.)

I wondered if there was some problem calling this off the EDT, but running in a new thread:

public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
public void run() {
ImageSelection.copyImageToClipboard(scaledImagePanel.getImage());
}
}).start();
}

just causes the new thread to hang instead:

"Thread-3" prio=6 tid=0x00514700 nid=0x18f6600 in Object.wait() [0xf0c89000..0xf0c89ad0]
at java.lang.Object.wait(Native Method)
- waiting on <0x26d635e0> (a apple.awt.OSXImageRepresentation)
at java.lang.Object.wait(Object.java:474)
at sun.awt.image.ImageRepresentation.reconstruct(ImageRepresentation.java:103)
- locked <0x26d635e0> (a apple.awt.OSXImageRepresentation)
at apple.awt.CDataTransferer.imageToPlatformBytes(CDataTransferer.java:238)
at sun.awt.datatransfer.DataTransferer.translateTransferable(DataTransferer.java:1180)
at apple.awt.CDataTransferer.translateTransferable(CDataTransferer.java:126)
at apple.awt.CClipboard.setContentsNative(CClipboard.java:67)
at sun.awt.datatransfer.SunClipboard.setContents(SunClipboard.java:93)
- locked <0x26d63528> (a apple.awt.CClipboard)
at e.util.ImageSelection.copyImageToClipboard(ImageSelection.java:16)
at e.tools.FatBits$CopyImageAction$1.run(FatBits.java:316)
at java.lang.Thread.run(Thread.java:613)

Amusingly, pasting into Mail at that point crashes Mail. (Somewhere in WebKit, thanks to which Mail seems to have made great strides forward in both its "crashing" and "incorrect text redraw" areas of functionality for 10.4.)

I couldn't find anything obviously related in Apple's release notes. (I'm using "1.5.0_02-36 mixed mode, sharing".) I didn't find anything pertinent with Google.

Investigating a bit more, I discovered that this worked:

ImageSelection.copyImageToClipboard(new
ImageIcon("/Users/elliotth/Desktop/nina.jpg").getImage());

So maybe it was relevant that my Image came from Robot.createScreenCapture via BufferedImage.getScaledInstance?

Removing the call to BufferedImage.getScaledInstance, I tried to put the result of Robot.createScreenCapture directly onto the clipboard. Preview's "New From Clipboard" showed a mangled image, as if it believed the image's width was greater than the amount pixel data per line. I'm sure you've all seen that kind of diagonal smear before.

Reverting, then removing the call to Robot.createScreenCapture and instead scaling an image loaded from disk showed me a similarly-mangled image in Preview.

So: two more Apple Java bugs, then?

1. Putting an Image that came from Image.getScaledInstance on the clipboard results in mangled images on paste (seemingly because of incorrect width information), regardless of where the original Image came from.

2. Putting an Image that came from Robot.createScreenCapture on the clipboard causes a hang in Apple's code.

Reported as Apple 4252224 (which I can't link to, because Apple aren't as enlightened as Sun).

Martin points out that Win32 hangs in a very similar place:

2005-09-11T19:41:55.845-0700 unknown: (hang #1) event dispatch thread stuck processing event for 1015 ms:
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Unknown Source)
sun.awt.image.ImageRepresentation.reconstruct(Unknown Source)
sun.awt.datatransfer.DataTransferer.imageToStandardBytes(Unknown Source)
sun.awt.windows.WDataTransferer.imageToPlatformBytes(Unknown Source)
sun.awt.datatransfer.DataTransferer.translateTransferable(Unknown Source)
sun.awt.windows.WDataTransferer.translateTransferable(Unknown Source)
sun.awt.windows.WClipboard.setContentsNative(Unknown Source)
sun.awt.datatransfer.SunClipboard.setContents(Unknown Source)
e.util.ImageSelection.copyImageToClipboard(ImageSelection.java:16)
e.tools.FatBits$CopyImageAction.actionPerformed(FatBits.java:314)

He also claims that Linux doesn't hang, but doesn't convince the GIMP that it's put an image on the clipboard.

I can't wait for my Ultra 20 to arrive so I can see what Solaris does!

At this point it looks like the hang (as opposed to the mangled image) is actually a cross-platform Sun bug. Searching for "sun.awt.image.ImageRepresentation.reconstruct" on the bug parade turns up a fair cluster of related bugs, mostly relating to animated GIF images (such as Sun 6176679).

Drawing the original image into a new BufferedImage and then putting that on the clipboard seems to avoid the hang:

public static void copyImageToClipboard(Image image) {
// Work around a Sun bug that causes a hang in "sun.awt.image.ImageRepresentation.reconstruct".
new javax.swing.ImageIcon(image); // Force load.
BufferedImage newImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
newImage.createGraphics().drawImage(image, 0, 0, null);
image = newImage;

ImageSelection imageSelection = new ImageSelection(image);
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.getSystemClipboard().setContents(imageSelection, null);
}

On Win32, you can paste the resulting image into pbrush.exe no problem. On Mac OS, Preview shows a mangled image. On Linux, GIMP offers to paste but silently fails to paste anything.

2005-10-07: Apple's latest Java 5 "preview" (they continue to lag years behind the rest of the world) fixes the image-mangling.

2005-09-10

Linux Java and Apple's X11

Although I've never had any trouble running Java programs on one Linux box and displaying them on another Linux box's display via X11, I'd never tried to display on a Mac until this week. I used ssh -X to connect to a machine at work from home, and tried xclock(1). Worked fine. Then I tried the program I actually wanted: SCM's check-in tool. Its window appeared, with the correct text in the title bar, but no visible window content.

If I ran a simple program, though, such as salma-hayek's DualTimeClock (which just has a JLabel for San Jose time and a JLabel for Bracknell time in a FlowLayout), it worked fine.

Whatever the problem is, it seems to be specific to Apple's X11 server, and it doesn't happen with all Swing programs. It's not just a network thing, though, because I had the same problem the next day at work when I tried to display on a Mac there from a Linux machine connected to the same switch.

Running Java 6 build 51 didn't help, either. (I had wondered if it could be anything to do with the X11 event loop problems mentioned in Sun bug 6193066.)

I'll have to investigate further when I've got a non-Mac at home, which should be as soon as next week...

...which is now. 2005-09-18. The first thing to do was to not use ssh -X, because that meant that all Ethereal could tell me was that there was encrypted traffic. Getting a naked connection to work was harder than I expected. xhost + wasn't sufficient. I know Debian Linux these days doesn't listen on the network, but netstat(1) showed that Apple's X11 was there on *:6000, tcp4 and tcp6. macosxhints to the rescue again, via Google, with the factoid that Apple's default IP firewall rules don't allow X11 traffic. (And Apple's firewall has no UI, other than that for configuring it. Nor does it keep logs by default.)

Normally, there's no traffic on the NETGEAR LAN. The Ultra 20 is the only device, and it connects to an IMAP server once a minute, but that's about it. So seeing hundreds of packets per second was a good clue that it wasn't just stuck. I left it about 50 seconds to see if it would come to a stop, and at that point a window appeared on the Mac's display. So it does actually work, it's just insanely slow.

A look at the capture shows a few TCP retransmissions, which is probably due to the wireless link between the NETGEAR and the Mac. But the real problem looks to be 6,160 ListFonts X11 requests out of the 15,760 packets. (Remember that there will be 6,160 ListFonts replies, too.)

Most of the ListFonts replies have a replylength of 0.

Anyway, running java in gdb on my Ultra 20 with a breakpoint on XListFonts, I saw that most of the backtraces were identical, and were in MToolkit. The two commonest backtraces (determined unscientifically; I haven't learned dtrace(1) yet) were this one:

#0 0xee757612 in XListFonts () from /usr/openwin/lib/libX11.so.4
#1 0xee331c75 in get_font_from_maxfonts () from /usr/openwin/lib/locale/common/xomLTRTTB.so.2
#2 0xee33220f in parse_fontname () from /usr/openwin/lib/locale/common/xomLTRTTB.so.2
#3 0xee332484 in create_fontset () from /usr/openwin/lib/locale/common/xomLTRTTB.so.2
#4 0xee332bfc in create_oc () from /usr/openwin/lib/locale/common/xomLTRTTB.so.2
#5 0xee756b29 in XCreateOC () from /usr/openwin/lib/libX11.so.4
#6 0xee75665d in XCreateFontSet () from /usr/openwin/lib/libX11.so.4
#7 0xeec778b2 in AWTIsHeadless () from /usr/jdk/instances/jdk1.5.0/jre/lib/i386/motif21/libmawt.so

And this one:

#0 0xee757612 in XListFonts () from /usr/openwin/lib/libX11.so.4
#1 0xeec7e5e8 in AWTCountFonts () from /usr/jdk/instances/jdk1.5.0/jre/lib/i386/motif21/libmawt.so
#2 0xee6a1c55 in Java_sun_font_NativeFont_haveBitmapFonts () from /usr/jdk/instances/jdk1.5.0/jre/lib/i386/libfontmanager.so

A quick export AWT_TOOLKIT=XToolkit later (see here), and I can display Java applications on my Mac without an insane amount of network traffic and an insanely long start-up time. At which point, I'm unconvinced that this is the problem I saw on Linux at work, because XToolkit is the default there. So now I guess I need to look at a network capture of that.

Update: Hans Liss from Uppsala University in Sweden explained the likely cause of my other problem. He points to Sun bug 4374153 where Dmitri Trembovetski mentions that you need ForwardX11Trusted set in your SSH configuration (somewhere under /etc, depending on your Unix). It turns out that some systems have this on by default while others default to off.