A little mouse hack

A major theme of the upcoming Firefox 4 release is improving speed and responsiveness, especially in the user interface. It’s easy to see issues where responsiveness is on the order of seconds (like waiting for an update to apply, or the length of time to respond to a modal dialog), but how do you examine and measure UI changes that happen over a period of just milliseconds?

One could instrument the code and get nanosecond-level precision — but it’s difficult to know where to insert probes, and you still may not be measuring what the user is actually seeing. A more direct “ground truth” would be to use a camera to record what’s displayed on the screen, and then examine the resulting video frame-by-frame to obtain data… I dabbled with this briefly last year when working on a Windows CE bug about slow-opening windows and menus. But more recently, Alex Faaborg has started a investigation of the Firefox UI using a significantly refined technique.

A problem Alex quickly ran into, though, is that it’s hard to look at a video and know exactly when the user clicked a mouse button. If only there was some visual indication of the mouse button state… Hmm! That was enough to start the wheels turning in my head, yadda yadda, and one weekend later I had complete a nice little hack that added some LEDs to a mouse so you could see the button states.

Some pictures… (See Flickr for the whole set.)

First, you’ve got to drill a nice hole (*)… I cringe every time I see someone mention doing some hack with a Dremel, because most of the time the result looks like something a drunk monkey with a chainsaw would produce.

Next, tap into the mouse’s microswitches (which are tied to ground) and the USB’s +5V.

Finally, route things nicely. Behold, the four-as… err, two-wired mouse!

This is the business end of the extra wire now attached to the mouse. 2 LEDs (and a couple of resistors) wrapped up in some heat-shrink tubing. When a mouse button is clicked, one of them lights up — red for right, (ye)llow for left.

This is a pretty simple little hack, but it was especially fun it’s not often us software guys get to do hardware stuff. And, hey, it even worked on the first try!

Sadly, there is one little problem… While the hacked mouse worked flawlessly for me, it immediately failed to work for Alex — the OS didn’t see mouse clicks, even though the LEDs lit up. Oh no! Luckily I had made the LED tail detachable (not shown here), as it turns out that if the mouse is plugged into the computer with the LED tail attached, it doesn’t work. But if it’s plugged in without the LED tail, and then the tail is plugged in afterwards, it works fine. I’m guessing that I just need to add some decoupling capacitor where I tapped into the switches, as the mouse’s IC was ok with the short traces to the switches but not the new 4-foot wires hanging off it. Or maybe a pull-up resistor? Both? Dunno exactly what the problem is yet — this is why I’m a software guy. 😉 What say you?

Advertisements

Brindisi

Blizzard tweeted this video of an operatic flash mob, which stuck a chord with me:

Why? I think it echos the best emotions around Firefox, the Mozilla Project, and open source… People doing what they love and feel passion for, providing that to people in everyday life, at no cost and for the betterment of the community, leading to sharing the mutual joy and delight of the result.

It can bring out the best of humanity.

On prompting, part 2

In the previous episode, I talked a bit about why I really, really don’t like the existing prompting code in Mozilla. I’ve been working on improving the situation.

The first big chunk of work is up for review in bug 563274. This basically removes all of the existing prompting code from /embedding, and creates a new component in /toolkit to replace it. The code is _much_ cleaner (though I may be biased!), and it’s written in JavaScript instead of C++.

For this first step, I’ve tried to keep things backwards-compatible and avoid breaking existing code. In fact, the UI is the exactly the same and the pile of nsIPrompt* / nsIAuthPrompt* interfaces are unchanged; there’s just a shiny new implementation underneath it all. But if anyone’s likely to find surprises, it’s probably embedders who are providing their own implementation of the existing code (eg, to implement their own non-XUL prompts). In theory those existing implementations should still work, but there might be some subtle bugs, build-goop changes, or patch dependencies on now-deleted code… The typical kinds of things that are expected when overhauling crufty old code, but these shouldn’t be hard to fix.

That said, this is just the first step. Upcoming steps are more likely to require changes that may break existing code. Even previously frozen interfaces like nsIPrompt / nsIPromptService might be thawed and changed (though we won’t take that step lightly). A few of the things I’m mulling over:

  • Make password manager watch prompt notifications, instead of implementing prompt interfaces.
  • Implementing tab-modal prompts.
  • Eliminate the nsIAuthPrompt-in-nsIAuthPrompt2 wrappers
  • Simplify the way chrome XHR handlers can override the default authentication prompting
  • Simplify the existing pile of interfaces (so instead of having 6 ways to do the same thing there are only 2 or 3. 🙂

More updates as plans solidify.

On the insanity of prompts

Some of the worst code I’ve had the misfortune of touching in the Mozilla codebase is code related to the handling of prompts. Specifically, the prompts involved with following interfaces: nsIPromptService, nsIAuthPrompt, nsIPromptFactory, nsIAuthPromptProvider, nsIPromptService2, nsIAuthPromptAdapterFactory.idl, etc. Just the plethora of confusing interfaces (for what is a fairly basic feature) is the first sign that something is amiss, and if you look at the actual methods on these interfaces you’ll quickly become lost in a maze of twisty little passages, all alike.

The implementation is spaghetti, too. As an example, let’s take a look at the sequence of functions called when JavaScript calls a simple little alert("Hello")

0) We begin our adventure in nsGlobalWindow::Alert().
1) Obtain an nsIPrompt via a do_GetInterface call on the docshell.
2) Ah, docshell doesn’t implement any of that, it just delegates to an nsIPrompt that it obtains from nsIWindowWatcher’s GetNewPrompter().
3) But GetNewPrompter() is actually just a one line function that calls NS_NewPrompter().
4) That’s over in nsPrompt.cpp, a short function that creates a new nsPrompt and initializes it.
5) Ok, now that we have an nsIPrompt (back from step 1), we can set up some strings and call its Alert() method.
6) …which doesn’t actually do anything, except to hand-off to nsIPromptService’s Alert().
7) This Alert() does some basic string twiddling, and next we end up in…
8) DoDialog(), which does more more twiddling, and FINALLY calls…
9) nsIWindowWatcher’s OpenWindow(), which opens a XUL window containing commonDialog.xul to do the prompt.

This is a little silly. And for outright insane, try tracing through the sequence of functions performed for showing an HTTP authentication prompt.

The good news is that I’ve been working on a large patch to simplify all this, and pave the way for some other interesting changes. More on this next time. 🙂