Tue, 24 Jul 2007

A quick intro to the Mugshot data model [14:21]

One thing I've been working on over the last few months is creating a flexible way for software running within the online desktop to retrieve information from the server. I've been calling this the "data model"; it's not a single piece of software, but rather a set of concepts, protocols, and software components that all fit together.

I posted an extended design document for the data model to the Mugshot mailing list before starting work on it. This post isn't meant to be a substitute for that, or for a reference manual, but just to give a little taste of what the data model is about.

The basic concepts in the data model are resources and property. A resource represents some thing: a user, an application, an addressbook entry. It is identified by an URI like http://mugshot.org/o/user/61m76k3hGbRRFS (the resource for my user account on mugshot.org). A resource has properties, which are just values identified by names; a Mugshot user resource has properties called name, photoUrl, contacts, and so forth. (Yes, the name "resource" is borrowed from RDF.)

You get information about resources by making queries. A query is a bit like a HTTP GET; you send a query name and a list of parameters to the server, and get a list of resources as a result. Just getting a list of resource URIs isn't that interesting, so you can also specify a "fetch string" with the query which says what property values to retrieve for each resource. If the property values point to additional resources, you can recursively specify what properties to retrieve from those resources. The most basic query is the 'getResource' query, which just gets property values from a single resource specified by its ID. For example, if you wanted to get my name and "home URL" from the mugshot.org server, and then also get the name and homeUrl for everybody in my contact list (my network), you could use the query:

 http://mugshot.org/p/system/getResource
   resourceId: http://mugshot.org/o/user/61m76k3hGbRRFS
   fetch: name;homeUrl;contacts [name;homeUrl]

The result from that is a list of resources and their properties; some of which are "direct" (query results), and some "indirect" (resources referenced by other resource properties)

  http://mugshot.org/o/user/hKcbRMYl4vNDqw (indirect)
     name: Colin
     homeUrl: http://mugshot.org/person?who=hKcbRMYl4vNDqw
  http://mugshot.org/o/user/gDmfAh8d7gXVRP (indirect)
     name: Havoc
     homeUrl: http://mugshot.org/person?who=gDmfAh8d7gXVRP          
  http://mugshot.org/o/user/61m76k3hGbRRFS (direct)
     name: Owen
     homeUrl: http://mugshot.org/person?who=61m76k3hGbRRFS
     contacts: http://mugshot.org/o/user/gDmfAh8d7gXVRP
     contacts: http://mugshot.org/o/user/hKcbRMYl4vNDqw

The thing that goes a beyond a HTTP GET is that a query doesn't just fetch property values, it also selects for future notification on those properties. So if Havoc changes his name on the server, I get notified immediately. This is important for the online desktop where there is no reload button and the user will expect information to just update by itself. Since the server tracks what it has sent the client, it can also avoid sending the same data over and over again.

I don't want to go into a lot of detail here about how the protocol works, how you add properties to the data model in the server, or how you access the data model from client code, so I'll just leave things here with a picture and a couple of code snippets. The picture:

Complete server code necessary to add the homeUrl property:

@DMProperty(defaultInclude=true, type=PropertyType.URL)
public String getHomeUrl() {
     return "/person?who=" + user.getId();
}

Client code in python for retrieving data from the model via the online desktop engine:

def on_got_self(resource):
    print resource.name, resource, resource.homeUrl
    for contact in resource.contacts:
        print "    ", contact.name, contact.homeUrl

model = DataModel()
query = model.query_resource(model.self_id, "name;homeUrl;contacts [name;homeUrl]")
query.add_handler(on_got_self)
query.execute()

Fri, 20 Jul 2007

Widget skeletons, GPU theming [12:42]

I agree 100% with Tim that using OpenGL directly as a rendering API for GTK+ makes no sense. The OpenGL 2D API is outdated and irrelevant; using the 3D API for acceleration of 2D is possible, but involves a lot of deep hackery; you want something like glitz to hide the details.

But does that mean there is no use for 3D rendering engines in the GTK+ world? I don't think that's the case. One idea I've been thinking about recently is that a 3D engine could provide a way of doing flexible theming in a way that is more accomodating of custom widgets than pixmap-based approaches. With a pixmap-based approach, if you know how to theme a button, that doesn't tell you how to to theme something that is somewhat like a button. For example, imagine that you had a button separated into two parts with a shallow groove in between, similar to what you often see for a drop-down on a browser's back button.

A theme that knows how to draw a button won't know how to draw a button with a groove. But if the custom widget calls the theme API not as "draw a button", but as "draw a button with this skeleton", where the skeleton is a two-and-a-half dimension shape like:

Then the theme can apply textures, pixel shaders, and possibly transformations to the shape to get the final appearance. The same approach that produces a button from the basic box shape should produce something reasonable from the custom skeleton.

(A poor quality mockup in the GIMP.) Note that with pixel shaders, you aren't restricted to pure 3D looks; it should be possible to get a flat high-contrast appearances if you want that. Another possible point of control for the theme engine would be modifying the skeleton a bit by, say, rounding off the corners.

This approach should allow a very wide range of themes; what it doesn't allow easily is themes that reproduce anything that a graphics designer could possibly dream up and draw in the GIMP or Photoshop. So, it would be very important to have interactive user-friendly tools for theming that allow the graphics designer to play around, get a sense of the possibilities and create themes without having to go through the intermediatary of a programmer.

Wed, 04 Apr 2007

Gimme, Bigboard, etc. [10:49]

Alex : I just wanted to quickly respond here to the points that you raise. First, Bigboard is not some huge skunkworks project; it's a prototype with slightly more than one person working on it for slightly more than one month, with all the source code in an open repository. And before we put any code down, we showed mockups and talked about our ideas at Fudcon.

But it's the fact that it is a prototype -- a place for us to try out our ideas, see what they look like on the screen live, and show them to others -- is why even now it's premature to worry too much about how it relates to Gimme. We didn't feel comfortable going to you and asking you to take let us take Gimme, require users to have an account on an unstable web service, add a hard dependency on a unstable custom canvas library, and redo the user interface to use these dependencies, in the process stripping away most of the polish you've added to Gimme. Maybe you'd have agreed, but I wouldn't have, if it was my project!

A common library for writing panel-like objects in Python and GTK+ would be possible, but I really don't see a big value there. It's not a lot of code and adding a common dependency would mostly be another hurdle to users trying Gimme or Bigboard out. The goal here isn't to have the minimal number of lines of code, beautiful architectural diagrams, and a coherent story about how things fit together from the start. Let's get the ideas out, see what works, what doesn't work, and where we we want to go. The architectural diagrams can be added later.

And because Gimme and Bigboard are both written in Python and GTK+, if the consensus ends up being that Bigboard isn't total crack, taking the good parts of Bigboard and integrating them into Gimme won't be a big deal.

Fri, 29 Sep 2006

Element identity and structured data in XML [10:18]

Like most people, my first experience with XML was with document formats: XHTML, SVG and so forth. So I've always had the idea that an XML element represents some sort of platonic ideal object: if you have an HTML anchor element or a SVG path element, it's well defined what it means, and what the content model is. But documents aren't the only use for XML. For mugshot, we pass lots of structured data between the server and client via XMPP, and between the server and AJAX web pages. And we often have things like:

<user userId="abcd12abcd" name="John Doe">
    <currentTrack>
        <artist>Johnny Cash</artist>
        <song>I Still Miss Someone</song>
        <playLink service="itunes" playlink="http://store.apple.com/..."/>
        <playLink service="yahoomusic" playlink="http://music.yahoo.com/..."/>
    </currentTrack>
    <favoriteTrack>
        <artist>The Beatles</artist>
        <song>Here Comes the Sun</song>
        <playLink service="itunes" playlink="http://store.apple.com/..."/>
    </favoriteTrack>
</user>

There are two weird things about the above from the “platonic ideal” perspective. First, the <currentTrack/> and <favoriteTrack/> elements don't really have any meaning other than the relationship of their content to the parent element. They are just “attribute” that happen to have structured XML data. The second thing is that <currentTrack/> and <favoriteTrack/> have the same structure, even though the element names are different. You could try to fix up the second problem and use a common element name:

<user userId="abcd12abcd" name="John Doe">
    <currentTrack>
	<track>
	     <artist>Johnny Cash</artist>
	     <song>I Still Miss Someone</song>
             <playLink service="itunes" playlink="http://store.apple.com/..."/>
        </track>
    </currentTrack>
    [...]
</user>

Or you could even try to fix both problems by using a generic element to represent a "XML-valued" attribute:

<user userId="abcd12abcd" name="John Doe">
    <attr name="currentTrack">
	<track>
	     <artist>Johnny Cash</artist>
	     <song>I Still Miss Someone</song>
             <playLink service="itunes" playlink="http://store.apple.com/..."/>
        </track>
    </attr>
    [...]
</user>

Or we could split things up and actually use an attribute:

<track id="track1">
     <artist>Johnny Cash</artist>
     <song>I Still Miss Someone</song>
     <playLink service="itunes" playlink="http://store.apple.com/..."/>
</track>
<user userId="abcd12abcd" name="John Doe" currentTrack="track1"/>

But I think all of those are essentially silly. The natural way to do structured data in XML is just different than a document use. When you have structured data: 1) some elements only have a local meaning in the context of their parent element. 2) there is a concept of “type” which is distinct from element identity.

Now, if there are any XML experts in the readership, they probably are now thinking that the above is some combination of blindingly obvious, woefully simplified, and hopelessly misguided. And they are doubtless right. But I found it a useful clarification of my thinking around XML design.

Sun, 18 Jun 2006

Just like Riding a Bike [19:50]

I've been putting the old saying "It's just like riding a bike, you never forget" to the test this weekend, having avoided that particular activity for the last 20 years. I'm considering buying a bike and doing some riding as a more air-cooled form of summer exercise, so I rented one this weekend to regain some basic ability. So far the results have been mixed as to whether it's possible to forget how to ride a bike: it only took me 45 minutes riding in circles around a parking lot to regain some ability to balance, turn, and stop, but the ground sure does seem further away than when I was twelve. Goal for tomorrow morning: relax my death grip on the handlebars and hopefully end up not feeling like I've been bench-pressing weights all day, as I do right now.

Update: moderate success. I certainly feel less trashed this evening than I did yesterday, after riding for somewhat longer today. I even saw one person on the trail that I was probably more skilled than. Of course, he was about 7 and using training wheels.

Mon, 12 Jun 2006

There are no harmless race conditions [11:01]

I spent most of Friday tracking down a problem we had been seeing with the Mugshot Windows client for months, where important parts of the client (Mugshot.exe) would mysteriously vanish from the system. This happened to both Bryan and Havoc when we pushed a new client release last week. Using information from that, combined with uninstalling and installing things about 200 times, I was able to track the problem down to the following sequence:

  1. When the upgrade starts, we tell the old client to exit
  2. The Windows Installer goes to remove the old version, and finds that Mugshot.exe is still in use, so it schedules a delete after the next reboot
  3. The old client actually finishes exiting.
  4. The Windows Installer installs the new files, and since Mugshot.exe is no longer in use, immediately replaces it with the new version, instead of scheduling that action to happen after reboot.
  5. The user is prompted to reboot the system, and after reboot, Mugshot.exe is deleted.

The fix, once tracked down, was pretty simple ... just actually wait for the old client to finish exiting before proceeding. Now, I knew when I originally wrote the code the race condition could happen, but convinced myself that It Will Be Very Rare: the Windows installer has all sorts of other work to do as well, and the client will exit quickly. And also It Will Be Harmless: maybe the user will be told to reboot unnecessarily, but that's all. As is almost always the case, neither was in fact true.

Other thing I should have known better about this weekend: when attending an outdoor music festival in the rain, waterproof footware will make the experience vastly more enjoyable, even if the music is worth it.

Tue, 07 Mar 2006

Do they have sheep in Burma? [21:01]

Warning: the following is not for those overly squeamish about dietary fat, nor for the ethnic cuisine purist.

I made the following stew on Sunday and have been feasting on the leftovers for the last two nights. It has no actual connection to Burma (or Myanmar), other than the combination of South and Southeast Asian flavors which I believe to be characteristic of the country. The inspiration here, other than the ingredients at hand, is the memory of the stews that one of the other students in my dorm at the University of Chicago would cook in the kitchen there. I don't know what part of Southeast Asia she was from — almost certainly not Burma — and I never got up the courage to ask to try on of the stews, but they always looked mysterious and smelled great.

The recipe should work as well with beef or even, I'd guess, goat. You don't want a fine cut of meat for this: in the long cooking, the fat and connective cookie dissolve and combine with the coconut milk into an luxurious, even unctuous broth.

I served this over a Japanese medium-grain rice, but a Southeast-Asian sticky rice, or even arborio would work as well. In fact, the combination of the creamy broth with moist and slightly chewy rice is reminiscent of risotto. I would avoid Basmati or Jasmine rice ... the broth should coat the rice, not be absorbed by it.

 "Burmese" Lamb Stew

  1.5lbs bone-in lamb stew, cut in 2 inch chunks

  1/3 cup canned coconut milk
    1 tbsp fish sauce
    1 small onion finely diced
    2 medium potatoes, peeled and cut into 1 inch chunks
    2 tsp fresh marjoram, finely chopped
  
    2 tsp fresh ginger, cut into fine shreds
    1 tsp ground coriander
  1/2 tsp turmeric
  1/4 tsp cayenne pepper 
    4 whole green cardamon pods
    2 bay leafs
    8 black peppercorns

      Peanut oil

 Brown the lamb on all sides in oil over medium-low heat in a 
 dutch oven or similar. Use multiple batches if necessary to 
 avoid overcrowding the pan. Remove lamb from pan and drain on 
 paper towels.

 Refresh oil if needed, add onion, and cook over low heat until
 the onion is soft and beginning to brown. Add ginger, whole and ground 
 dried spices and saute briefly until the spices are fragrant.
 Add lamb and 3-4 cups water (enough to just cover the lamb), bring
 to a boil, reduce heat, and simmer partially covered for 2 hours.
 
 Add potatoes and fish sauce, cook for 20 more minutes until the 
 potatoes are cooked, add coconut milk, return to a simmer, taste
 and add salt if the dish is insufficiently salty from the
 fish sauce.

 Serve over medium grain rice with a side salad of yogurt, cucumbers,
 and finely chopped red onion. Makes 3 small but rib-sticking 
 portions.

Notes: All quantities above are approximate and from memory. The lamb should be completely tender and coming off the bone, but the potatoes should keep their shape. Don't eat the cardamon pods, bay leaves, or peppercorns.

Sun, 11 Sep 2005

Seen while washing dishes [21:34]

Salt Crystals

Ice on a puddle? No, it's salt crystals I found this morning in the bottom of my sphaghetti pot. A little bit of salty water had dried up leaving these crystals behind. There's several interesting things about the image to me: it demonstrates very well the idea of domains - crystals started at various nucleation points and grew out and met each other. It's also interesting to see different growth behaviors in different parts of the pot, probably depending on drying rate and salt concentration. In some areas you have long linear crystals; in other areas the growth is more compact. Most of the crystals branch at 90 degree angles, but a few grow in a more spidery pattern. (Somewhat reminiscent of patterns you get in diffusion limited aggregation simulations.)

Fri, 29 Apr 2005

GUADEC registration fee [10:07]

First let me say that I don't think anyone is entirely happy with the registration fee situation. Obviously the fact that payment is only by paypal is far from ideal. I'm not completely in agreement with where the registration levels were set. The website is confusing. But it's not an attempt to rip people off or discourage people from coming.

Let me explain the levels in a little more detail: there are three levels:

We're not going to investigate who registers as a hobbyist. We want all members of the GNOME community at GUADEC, so if paying the the professional fee poses a problem for you in any way (moral, financial, typographic), please register as a hobbyist.

So, why charge registration fees at all? The basic reason is that plane tickets are expensive. While our sponsors largely cover costs like the the venue, if we take in more money, we can bring in more GNOME hackers from around the world. I think it's very reasonable to ask people for who are coming to GUADEC as part of their job to help bring to GUADEC those GNOME contributors that don't have a company to sponsor them.

Thu, 07 Apr 2005

Cooking [23:42]

My cousin Alice from Germany has been staying me this week and exploring Boston, and we've done quite a bit of cooking in the evenings.

On Saturday we cooked Indian: a dish of tomatoes and masoor daal (based on a recipe titled rasam, but it didn't come out remotely like that, though it was tasty enough), potatos and eggplant with fenugreek seeds, and rice with fresh fenugreek leaves.

Monday, I made one of my favorite dishes ... a Southeast Asian (quasi-Vietnamese) chicken soup with rice noodles. It's all about the garnishes: we had bean sprouts, fresh mint and coriander, sliced chiles, lime wedges, and ginger paste.

Tuesday, we made a dish that Alice learned from her brother: rigatoni with winter squash. You basically cook the squash until it is soft, mash it with parmesan and black pepper, and then mix in the cooked pasta. Very good and not at all something that I'd normally make myself. We made it with buttercup squash which gave the whole dish a slightly flourescent yellowish-green color. Apparently it is even better when made with pumpkin.

On Wednesday we cooked cod with the remaining fresh fenugreek, together with couscous with dried cranberries (something that I picked up from Rosanna and Jonathan recently), and a relish of apple, minced serrano peppers, mint, and lemon juice. A real treat and less than a half hour of total preparation and cooking time.

Tonight we took leftover mashed squash from the pasta, and combined it with leftover chicken stock from the soup on Monday night, cooked it for a while with some pieces of star anise, then pureed it, and added a bit of cream. Voila, winter squash soup. We had that with good bread from Whole Foods and a cheese and bean sprout omelet.

All in all, it's been a fun and tasty week.

Fri, 03 Dec 2004

Pagers in Luminocity [14:37]

I've been spending some time again this week on Luminocity; mostly redoing the texture handling code. With the rewrite, it can break windows larger than the max allowed texture size into multiple tiles, and I've also added mipmap support. After getting the mipmaps working, I quickly hacked up live thumbnailing pagers:

Hinting viewer screenshot

From a code perspective, that screenshot doesn't have 4 pagers. It's 5 pagers, one of which happens to be full screen. The effect is similar to what Enlightenment and the GNOME pager had several years ago, but the larger size, higher quality scaling, and immediate updates makes it feel a lot nicer. That's a lot of screen real estate to take up on the screen, of course; expanding the pager on mouseover might work nicely.

Sat, 20 Nov 2004

Rotated text in GTK+ and GDK [17:50]

Many years ago, when Microsoft first added TrueType support to Windows, I remember being very impressed by a demo where a text string was rotated and drawn in different colors. Simple stuff, really, but something that we've never been able to do in GTK+ because of the limitations of the ancient X drawing API. (Crude support was added for rotated glyphs for core X fonts eventually, but who wants rotated, non-antialiased text? Can you imagine anything more ugly?) A lot of the pieces have been falling into place over the last few years: GTK+-2.4 required Xft and no longer support core X fonts. Pango-1.6 added PangoMatrix and implemented rotated rendering for the FT2 backend. Over the summer, I added PangoRenderer to Pango, which abstracts out all the positioning, attribute parsing, and so forth in rendering a PangoLayout, making adding rotated rendering for additional backends a lot easier. What was left was writing a PangoRenderer for GDK and some API to rotate GtkLabel. I finally got around to that this week.

rotated text screenshot     rotated label screenshot

I've always been just a little confused by transformations when drawing; if I want cairo_rectangle (cr, 0, 0, 1, 1) to drawn a 50 by 50 square at 100 by 100, is that?

cairo_translate (cr, 100, 100);
cairo_scale (cr, 50, 50);

Or is it?

cairo_scale (cr, 50, 50);
cairo_translate (cr, 100, 100);

When I added PangoMatrix to Pango, I decided I was fed up by continually having to try everything both ways, sat down, worked through various examples, and really got it straight in my head. And because of that, I got all the transformations used in the rotated text example above right the first time. A new experience. So, what's the secret? What helps me is to keep two distinct but equivalent formulations in mind. (I'm talking in terms of Cairo here, but Cairo, Java2D, gnome-print, Postscript, etc all borrowed Postscript's conventions, so they are the same.) The first formulation defines how the matrices work.

The current transformation matrix gives the transformation from user coordinates (the ones you pass to drawing functions) to device coordinates (the ones that determine what pixels are drawn). Mathematically:

p_device = M * p_user

When you call a Cairo transformation function, that gets added on the user coordinate side. Mathematically:

p_device = M_orig * M_new * p_user

So, say we call cairo_translate (cr, 100, 100) then cairo_scale (cr, 50, 50). We get a transformation that first scales by 50 times in each direction, then translates by 100, 100. That formulation is good to have available, especially if you need to handle the matrices directly (as when implementing bits of Pango or Cairo.) And you can use to check to see which order of the two I quoted is right by taking the point 1,1 and putting it through the two transformations. But it's not entirely intuitive: you have to think about the transformations in the reverse order they appear in the code. For, writing code what I find easier is:

When you call a Cairo transformation function, that scales/ rotates/translates the axes of the user space coordinate system with respect to themselves.

With that formulation, in mind, it's easier to see that the order we need is the first one. cairo_translate (cr, 100, 100) shifts the coordinate origin to 100, 100. cairo_scale (cr, 50, 50); then scales up the coordinate axes by 50 times in each direction, leaving the coordinate origin in the same place. (This all would be clearer with a diagram...)

Now if I only could shake the feeling that by writing this I'm courting a collision with a graphics system that uses the opposite convention...

Sat, 13 Nov 2004

Boot poster challenge [12:19]

When it comes to trying to speed up system startup, it often seems to me that the work people do is based on blind guesses as to what the actual bottlenecks are. What I've really wanted to see is a graphical representation of the boot process along the lines of some of the examples that Edward Tufte uses. I've now written this up in detail along with an offer to pay for the printing of a hard copy if someone picks up the challenge.

Wed, 10 Nov 2004

Screenshot Update [20:16]

Here's an update of my Luminocity screenshot from a few weeks ago:

Hinting viewer screenshot

As you can see, the main difference is, as promised, window decorations. What doesn't show up in the screenshot is just how nice resizing with those decorations is. No flash, no flicker, no jitter, no redraws. I could sit there for hours making windows bigger and smaller....

Fri, 05 Nov 2004

Texture profiling [16:19]

With the work I've been doing recently with luminocity, I realized that I had absolutely no idea of what was going on for video card memory usage. Rather than putting some one-off print statements into the card driver, I decided to try and make something a little more reusable and convenient. That Sunday-afternoon project ended up taking most of this week, but I'm pretty happy with the result: texturetop. The obligatory screenshot:

texturetop screenshot

In order to have a decent chance of getting this upstream and out of my hands, texturetop is a text-mode client. No GTK+, no GLib. Actually, texturetop is just a minimal front end. The somewhat larger piece of work was adding code to Mesa/DRI to get it to connect to texturetop and provide the information. libGL and texturetop talk a text-based protocol over a socket, so it was an opportunity to dust off my “how to listen on a socket” skills as well as my “how to write a linked list by hand” skills.

We have dozens of profiling tools on Linux, from oprofile and valgrind, to memprof, xrestop, and texturetop. Is there some way that a "profiling console" could be created that would provide access to all this disparate information and integrate it together?

Tue, 02 Nov 2004

Virtual events [11:45]

One of the central objects in the GLib main loop is GSource. What is GSource the source of? Events. But where are the events? There is no event object, and the docs very seldom mention events (though they do frequently talk about “event sources”.) What I realized last night is that there really are events in the GLib main loop, they are just virtual: a concept without an object behind it.

A lot of the GLib main loop becomes clearer when viewed in this light: the return value of the ‘check’ and ‘prepare’ members of GSourceFuncs isn’t “TRUE if it is ready to be dispatched”, but rather “TRUE if there are any pending events for this source”. g_source_set_can_recurse() actually sets whether the source can have multiple outstanding events. Recursion becomes a whole lot more conceptually simple: we’re simply pulling more events off the event queue and processing them before we finish processing the last one.

Of course, because I didn’t realize this when I created the main loop interface in 1998, or when I revised it for GLib-2.0 in 2000, there are some places where the match isn’t exactly perfect. For example, g_main_context_dispatch() should dispatch only the single event at the front of the (virtual) event queue: instead it dispatches one event from each GSource that has pending events and who’s priority matches the priority of the highest priority GSource that has pending events. (whew!)

Mon, 01 Nov 2004

Luminocity [19:18]

Over the last week or so, I've been working on a new toy, a combination window manager and compositing manager with GL output called 'luminocity'. Note the word toy: I have no intention of ever worrying about all the edge cases and broken legacy clients that you need in a real window manager. I do, however, intend to add title bars.

Hinting viewer screenshot

Despite the quick-hack nature of luminocity, I've had quite a bit of fun with the code. It's nicely modularized with lots of GObjects and signals. It has a separate rendering thread for the GL output. There is some advanced Xlib programming to keep everything async. And it uses X extensions by the boatload: XFIXES, SYNC, DAMAGE, Composite, XTEST. (And GLX, of course.)

The way that luminocity works is that you run luminocity against an offscreen X display (I've been using the kdrive based Xfake X server.) It asserts itself as the window manger and compositing manager for the display and pops up the output in a window. Input is forward back from the window to the offscreen display. This makes for a very cool development process. You run some apps against the offscreen server. You start luminocity, look at how the apps look. Play with them, move them around. You quit luminocity, make some code changes, restart luminocity. The apps are still there. It's a lot of fun.

What's next? Title bars. Some effects to take advantage of GL - animations and live minimization. Some work studying the performance bottlenecks. (Texture swapfests seem to be a problem at the moment.) Maybe some work on application synchronization. But in the end, the work that Søren is doing on Metacity's CM is much more likely to be the permanent way forward. Or maybe Looking Glass. Luminocity has about the same architecture as Sun's Looking Glass, it's just much simpler and smaller, all written in C, and I have no plans to add 3D client support to it.

Mon, 06 Sep 2004

Building a tool [21:29]

I'm thinking on doing some more work on the FreeType font hinting code sometime soon, so I've spent some time the last few weekends building a program to view hinted outlines. The way that hinting fonts for the screen works is that the outline is modified to better fit the grid of pixels, then the modified outline is scan-converted. My program allows looking at a magnified version of the modified outline and comparing it to the resulting pixels.

Hinting viewer screenshot

As you might guess from the spiffy, antialiased, alpha-blended graphics, I'm using Cairo to do the main drawing. It's been a lot of fun to write a very small, limited purpose application and really nail the details. To list a few:

I was suprised by how well saving the entire application state to GConf worked. There is no separation between preferences and transient state. You can just exit the viewer at any time and bring it up and it remembers where you were. This clearly isn't the right model for most applications, but I wonder if there are places in the GNOME desktop where idea could be effectively applied; perhaps desk accessories like gcalctool, gconf-editor, and gucharmap should work like that. For the viewer, the code flow for interacting with GConf is a little unusual; instead of monitoring GConf actively as the "model" of the application, the viewer simply initializes from GConf on startup and saves changes to GConf as they happen. This allows having multiple viewers usefully running at once.

Sun, 08 Aug 2004

Sweet Corn [00:29]

One of the best benefits of being back up North is the ability to get good, fresh sweet corn. Now, I need to say here that corn sold in a supermarket is almost certainly not fresh. Corn that wasn't picked the same day you eat it is not fresh. If you have ever ordered an item that comes with "corn on the cob" in a restaurant in January, that's a good sign that you have no idea what real corn tastes like. With that definition out of the way, I never was able to find a single decent ear of corn in the 6 years I lived in North Carolina. The few times I actually found local corn at a farmer's market or farm stand it was a poor, undergrown, worm-eaten approximation of the real thing. I don't know if the problem down there was climate, soil, or culture. But no problem here in Massachusetts so far.

So, how do you cook and eat corn? You husk it, drop it in boiling water for as short as time as possible (5-6 minutes is about right), butter it, salt it, and as soon as you can pick up the ear without burning yourself, dive in. I take no position on eating pattern; linear, spiral, and random access all work.

Corn goes well with other summer flavors. Tomatoes, fresh basil, grilled meats, and so forth. In fact, fresh corn and fresh tomatoes, with nothing but butter and salt for the corn and salt for the tomatoes is a meal fit for a king. I haven't seen good tomatoes yet this year, but I've had good luck the last few weeks with other accompaniments. Last Saturday, in celebration of the beginning of corn season, I had broiled lamb chops with a peach-habeñero salsa. (slightly green peach, onion, finely chopped habeñero, red wine vinegar). This week, I made a Thai-inspired sautée of scallops with basil, garlic, fish sauce, and strips of chili. Both were easy to make, really tasty. Both went well with the corn. In the end analysis I probably preferred the scallops because they distracted less from the corn. Which, after all, was the real point of the meal. Most things you can cook 52 weeks a year these days. Corn season is 6 weeks a year, if you are lucky. Or 6 weeks after 6 years if you've been living down south.