Shallow Thoughts

Akkana's Musings on Open Source, Science, and Nature.

Sat, 16 Aug 2008

Fast Pixel Ops in GIMP-Python

Last night Joao and I were on IRC helping someone who was learning to write gimp plug-ins. We got to talking about pixel operations and how to do them in Python. I offered my arclayer.py as an example of using pixel regions in gimp, but added that C is a lot faster for pixel operations. I wondered if reading directly from the tiles (then writing to a pixel region) might be faster.

But Joao knew a still faster way. As I understand it, one major reason Python is slow at pixel region operations compared to a C plug-in is that Python only writes to the region one pixel at a time, while C can write batches of pixels by row, column, etc. But it turns out you can grab a whole pixel region into a Python array, manipulate it as an array then write the whole array back to the region. He thought this would probably be quite a bit faster than writing to the pixel region for every pixel.

He showed me how to change the arclayer.py code to use arrays, and I tried it on a few test layers. Was it faster? I made a test I knew would take a long time in arclayer, a line of text about 1500 pixels wide. Tested it in the old arclayer; it took just over a minute to calculate the arc. Then I tried Joao's array version: timing with my wristwatch stopwatch, I call it about 1.7 seconds. Wow! That might be faster than the C version.

The updated, fast version (0.3) of arclayer.py is on my arclayer page.

If you just want the trick to using arrays, here it is:

from array import array

[ ... setting up ... ]
        # initialize the regions and get their contents into arrays:
        srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,
                                     False, False)
        src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])

        dstRgn = destDrawable.get_pixel_rgn(0, 0, newWidth, newHeight,
                                            True, True)
        p_size = len(srcRgn[0,0])               
        dest_pixels = array("B", "\x00" * (newWidth * newHeight * p_size))

[ ... then inside the loop over x and y ... ]
                        src_pos = (x + srcWidth * y) * p_size
                        dest_pos = (newx + newWidth * newy) * p_size
                        
                        newval = src_pixels[src_pos: src_pos + p_size]
                        dest_pixels[dest_pos : dest_pos + p_size] = newval

[ ... when the loop is all finished ... ]
        # Copy the whole array back to the pixel region:
        dstRgn[0:newWidth, 0:newHeight] = dest_pixels.tostring() 

Good stuff!

Tags: , , ,
[ 21:02 Aug 16, 2008    More gimp | permalink to this entry ]

Sat, 09 Aug 2008

GetSET: Teaching Javascript to high school girls

Every summer I volunteer as an instructor for a one-day Javascript programming class at the GetSET summer technology camp for high school girls. GetSET is a great program run by the Society of Women Engineers. it's intended for minority girls from relatively poor neighborhoods, and the camp is free to the girls (thanks to some great corporate sponsors). They're selected through a competitive interview process so they're all amazingly smart and motivated, and it's always rewarding being involved.

Teaching programming in one day to people with no programming background at all is challenging, of course. You can't get into any of the details you'd like to cover, like style, or debugging techniques. By the time you get through if-else, for and while loops, some basic display methods, the usual debugging issues like reading error messages, and typographical issues like "Yes, uppercase and lowercase really are different" and "No, sorry, that's a colon, you need a semicolon", it's a pretty full day and the students are saturated.

I got drafted as lead presenter several years ago, by default by virtue of being the only one of the workshop leaders who actually programs in Javascript. For several years I'd been asking for a chance to rewrite the course to try to make it more fun and visual (originally it used a lot of form validation exercises), and starting with last year's class I finally got the chance. I built up a series of graphics and game exercises (using some of Sara Falamaki's Hangman code, which seemed perfect since she wrote it when she was about the same age as the girls in the class) and it went pretty well. Of course, we had no idea how fast the girls would go or how much material we could get through, so I tried to keep it flexible and we adjusted as needed.

Last year went pretty well, and in the time since then we've exchanged a lot of email about how we could improve it. We re-ordered some of the exercises, shifted our emphasis in a few places, factored some of the re-used code (like windowWidth()) into a library file so the exercise files weren't so long, and moved more of the visual examples earlier.

I also eliminated a lot of the slides. One of the biggest surprises last year was the "board work". I had one exercise where the user clicks in the page, and the student has to write the code to figure out whether the click was over the image or not. I had been nervous about that exercise -- I considered it the hardest of the exercises. You have to take the X and Y coordinates of the mouse click, the X and Y coordinates of the image (the upper left corner of the <div> or <img> tag), and the size of the image (assumed to be 200x200), and turn that all into a couple of lines of working Javascript code. Not hard once you understand the concepts, but hard to explain, right?

I hadn't made a slide for that, so we went to the whiteboard to draw out the image, the location of the mouse click, the location of the image's upper left corner, and figure out the math ... and the students, who had mostly been sitting passively through the heavily slide-intensive earlier stuff, came alive. They understood the diagram, they were able to fill in the blanks and keep track of mouse click X versus image X, and they didn't even have much trouble turning that into code they typed into their exercise. Fantastic!

Remembering that, I tried to use a lot fewer slides this year. I felt like I still needed to have slides to explain the basic concepts that they actually needed to use for the exercises -- but if there was anything I thought they could figure out from context, or anything that was just background, I cut it. I tried for as few slides as possible between exercises, and more places where we could elicit answers from the students. I think we still have too many slides and not enough "board work" -- but we're definitely making progress, and this year went a lot better and kept them much better engaged. We're considering next year doing the first several exercises on the board first, then letting them type it in to their own copies to verify that it works.

We did find we needed to leave code examples visible: after showing slides saying something like "Ex 7: Write a loop that writes a line of text in each color", I had to back up to the previous slide where I'd showed what the code actually looked like. I had planned on their using my "Javascript Quick Reference" handout for reference and not needing that information on the slides; but in fact, I think they were confused about the quickref and most never even opened it. Either that information needs to be in the handout, or it needs to be displayed on the screen as they work, or I have to direct them to the quickref page explicitly ("Now turn to page 3 in ...") or put that information in the exercises.

The graphical flower exercises were a big hit this year (I showed them early and promised we'd get to them, and when we did, just before lunch, several girls cheered) and, like last year, some of the girls who finished them earlier decided on their own that they wanted to change them to use other images, which was also a big hit. Several other girls decided they wanted more than 10 flowers displayed, and others hit on the idea of changing the timeout to be a lot shorter, which made for some very fun displays. Surprisingly, hardly anyone got into infinite loops and had to kill the browser (always a potential problem with javascript, especially when using popups like alert() or prompt()).

I still have some issues I haven't solved, like what to do about semicolons and braces. Javascript is fairly agnostic about them. Should I tell the girls that they're required? (I did that this year, but it's confusing because then when you get to "if" statements you have to explain why that's different.) Not mention them at all? (I'm leaning toward that for next year.)

And it's always a problem figuring out what the fastest girls should do while waiting for the rest to finish. This year, in addition to trying to make each exercise shorter, we tried having the girls work on them in groups of two or three, so they could help each other. It didn't quite work out that way -- they all worked on their own copies of the exercises but they did seem to collaborate more, and I think that's the best balance. We also encourage the ones who finish first to help the girls around them, which mostly they do on their own anyway.

And we really do need to find a better editor we can use on the Windows lab machines instead of Wordpad. Wordpad's font is too small on the projection machine, and on the lab machines it's impossible for most of us to tell the difference between parentheses, brackets and braces, which leads to lots of time-wasting subtle bugs. Surely there's something available for Windows that's easy to use, freely distributable, makes it easy to change the font, and has parenthesis and brace matching (syntax highlighting would be nice too). Well, we have a year to look for one now.

All in all, we had a good day and most of the girls gave the class high marks. Even the ones who concluded "I learned I shouldn't be a programmer because it takes too much attention to detail" said they liked the class. And we're fine with that -- not everybody wants to be a programmer, and the point isn't to force them into any specific track. We're happy if we can give them an idea of what computer programming is really like ... then they'll decide for themselves what they want to be.

Tags: , , ,
[ 11:54 Aug 09, 2008    More education | permalink to this entry ]

Sun, 25 May 2008

Crikey in Python, and generating key events with XTest

A user on the One Laptop Per Child (OLPC, also known as the XO) platform wrote to ask me how to use crikey on that platform.

There are two stages to getting crikey running on a new platform:

  1. Build it, and
  2. Figure out how to make a key run a specific program.

The crikey page contains instructions I've collected for binding keys in various window managers, since that's usually the hard part. On normal Linux machines the first step is normally no problem. But apparently the OLPC comes with gcc but without make or the X header files. (Not too surprising: it's not a machine aimed at developers and I assume most people developing for the machine cross-compile from a more capable Linux box.)

We're still working on that (if my correspondant gets it working, I'll post the instructions), but while I was googling for information about the OLPC's X environment I stumbled upon a library I didn't know existed: python-xlib. It turns out it's possible to do most or all of what crikey does from Python. The OLPC is Python based; if I could write crikey in Python, it might solve the problem. So I whipped up a little key event generating script as a test.

Unfortunately, it didn't solve the OLPC problem (they don't include python-xlib on the machine either) but it was a fun exercises, and might be useful as an example of how to generate key events in python-xlib. It supports both event generating methods: the X Test extension and XSendEvent. Here's the script: /pykey-0.1.

But while I was debugging the X Test code, I had to solve a bug that I didn't remember ever solving in the C version of crikey. Sure enough, it needed the same fix I'd had to do in the python version. Two fixes, actually. First, when you send a fake key event through XTest, there's no way to specify a shift mask. So if you need a shifted character like A, you have to send KeyPress Shift, KeyPress a. But if that's all you send, XTest on some systems does exactly what the real key would do if held down and never released: it autorepeats. (But only for a little while, not forever. Go figure.)

So the real answer is to send KeyPress Shift, KeyPress a, KeyRelease a, KeyRelease Shift. Then everything works nicely. I've updated crikey accordingly and released version 0.7 (though since XTest isn't used by default, most users won't see any change from 0.6). In the XSendEvent case, crikey still doesn't send the KeyRelease event -- because some systems actually see it as another KeyPress. (Hey, what fun would computers be if they were consistent and always predictable, huh?)

Both C and Python versions are linked off the crikey page.

Tags: , , ,
[ 14:50 May 25, 2008    More programming | permalink to this entry ]

Tue, 18 Mar 2008

Setting app name and class in Xlib

I was looking at Dave's little phase-of-the-moon Mac application, and got the urge to play with moonroot, the little xlib ditty I wrote several years ago to put a moon (showing the right phase) on the desktop.

I fired it up, and got the nice moon-shaped window ... but with a titlebar. I didn't want that! Figuring out how to get rid of the titlebar in openbox was easy, just

<application name="moonroot">
    <decor>no</decor>
    <desktop>all</desktop>
</application>
... but it didn't work! A poke with xwininfo showed the likely cause: instead of "moonroot", the window was listed as "Unnamed window". Whoops!

A little poking around revealed three different ways to set "name" for a window: XStoreName, XSetClassHint (which sets both class name and app name), and XSetWMName. Available online documentation on these functions was not very helpful in explaining the differences; fortunately someone hanging out on the openbox channel knew the difference (thanks, Crazy_Hopper). Thus:

I didn't see much in the way of example code for what an app ought to do with these, so I'll post mine here:

    char* appname;
    XClassHint* classHint;
[ ... ]
    if (argv && argc > 1)
        appname = basename(argv[0]);
    else
        appname = "moonroot";

    /* set the titlebar name */
    XStoreName(dpy, win, appname);

    /* set the name and class hints for the window manager to use */
    classHint = XAllocClassHint();
    if (classHint) {
        classHint->res_name = appname;
        classHint->res_class = "MoonRoot";
    }
    XSetClassHint(dpy, win, classHint);
    XFree(classHint);

And if anyone is interested in my silly moon program, it's at moonroot-0.3.tar.gz. moonroot gives you a large moon, moonroot -s gives a smaller one. I'm not terribly happy with its accuracy and wasted too much time today fiddling with it and verifying that it's doing the right time conversions. All I can figure is that the approximation in Meeus' Astronomical Algorithms is way too approximate (it's sometimes off by more than a day) and I should just rewrite all my moon programs to calculate moon phase the hard (and slow) way.

Tags: , ,
[ 20:15 Mar 18, 2008    More programming | permalink to this entry ]

Fri, 29 Feb 2008

Script to add tags

Python is so cool. I love how I'll be working on a script and suddenly think "Oh, it should also do X, but I bet that'll be a lot more work", and then it occurs to me that I can do exactly that by adding about 2 more lines of python. And I add them and it works the first time.

Anyway, it turned out to be very easy to go through all existing blog articles and add tags for the current category hierarchy, being careful to preserve each file's last-modified date since that's what pyblosxom uses for the date of the entry. add-tags.py

Tags: ,
[ 18:37 Feb 29, 2008    More blogging | permalink to this entry ]

Wed, 27 Feb 2008

Got tags working with pybloxsom

Entries on this blog are arranged by category. But all too often I have something that really belongs equally well in two categories. Since pyblosxom's categories follow the hierarchy on disk, there's no way to have an entry in two categories. Enter tags.

Tags are a way of assigning any number of keywords to each blog entry. Search engines apparently pay attention to tags, and most tagged blogs also let you search by tag.

I wanted my tags to follow whatever canonical tag format the big blogging sites use, so search engines would index them. Unfortunately, this isn't well documented anywhere. Wikipedia has a tags entry that mentions a couple of common formats; the HTML format given in that entry (<a rel="tag" ...>) turns out to be the format used on most popular sites like livejournal and blogspot, so that's what I wanted to use. Later, someone pointed me to a much better tag explanation on technorati, which is useful whether or not you decide to register with technorati.

Next: how to implement searching? The simplest pyblosxom tags plug-in is called simply tags.py. All the others are much more complex and do tons of things I'm not interested in. But tags.py doesn't support static mode, and points to a modified tags.py that's supposedly modified to work with static blogs.

Alas, when I tried that version, it didn't work (and an inquiry on the pybloxsom list got a response from someone who agreed it didn't work). So I hacked around and eventually got it working. Here's a diff for what I changed or just the tags-static.py plug-in.

Additional steps I needed that weren't mentioned in tags.py:

I also wrote a little python index.cgi for my blog's /tags directory, so you can see the list of tags used so far. Strangely, tags.py didn't create any such index, and it was easier to make a cgi than to figure out how to do it from a blosxom plug-in.

And as long as I'm posting pyblosxom diffs, here's the little filename diff for 1.4.3 that I apply to pyblosxom whenever I update it, to let me use the .blx extension rather than .txt for my blog source files. (That way I can configure my editor to treat blog files as html, which they are -- they aren't plaintext.)

Anyway, it all seems to be working now, and in theory I can tag all future articles. I'll probably go back and gradually add tags to older articles, but that's a bigger project and there's no rush.

Tags: ,
[ 15:04 Feb 27, 2008    More blogging | permalink to this entry ]

Fri, 12 Oct 2007

PyTopo and PyGTK pixbuf memory leakage

On a recent Mojave desert trip, we tried to follow a minor dirt road that wasn't mapped correctly on any of the maps we had, and eventually had to retrace our steps. Back at the hotel, I fired up my trusty PyTopo on the East Mojave map set and tried to trace the road. But I found that as I scrolled along the road, things got slower and slower until it just wasn't usable any more.

PyTopo was taking up all of my poor laptop's memory. Why? Python is garbage collected -- you're not supposed to have to manage memory explicitly, like freeing pixbufs. I poked around in all the sample code and man pages I had available but couldn't find any pygtk examples that seemed to be doing any explicit freeing.

When we got back to civilization (read: internet access) I did some searching and found the key. It's even in the PyGTK Image FAQ, and there's also some discussion in a mailing list thread from 2003.

Turns out that although Python is supposed to handle its own garbage collection, the Python interpreter doesn't grok the size of a pixbuf object; in particular, it doesn't see the image bits as part of the object's size. So dereferencing lots of pixbuf objects doesn't trigger any "enough memory has been freed that it's time to run the garbage collector" actions.

The solution is easy enough: call gc.collect() explicitly after drawing a map (or any other time a bunch of pixbufs have been dereferenced).

So there's a new version of PyTopo, 0.6 that should run a lot better on small memory machines, plus a new collection format (yet another format from the packaged Topo! map sets) courtesy of Tom Trebisky.

Oh ... in case you're wondering, the ancient USGS maps from Topo! didn't show the road correctly either.

Tags: , , ,
[ 21:21 Oct 12, 2007    More programming | permalink to this entry ]

Tue, 04 Sep 2007

Egg Timer in Python and TkInter

I left the water on too long in the garden again. I keep doing that: I'll set up something where I need to check back in five minutes or fifteen minutes, then I get involved in what I'm doing and 45 minutes later, the cornbread is burnt or the garden is flooded.

When I was growing up, my mom had a little mechanical egg timer. You twist the dial to 5 minutes or whatever, and it goes tick-tick-tick and then DING! I could probably find one of those to buy (they're probably all digital now and include clocks and USB plugs and bluetooth ports) but since the problem is always that I'm getting distracted by something on the computer, why not run an app there?

Of course, you can do this with shell commands. The simple solution is:

(sleep 300; zenity --info --text="Turn off the water!") &

But the zenity dialogs are small -- what if I don't notice it? -- and besides, I have to multiply by 60 to turn a minute delay into sleep seconds. I'm lazy -- I want the computer to do that for me!
Update: Ed Davies points out that "sleep 5m" also works.

A slightly more elaborate solution is at. Say something like: at now + 15 minutes and when it prompts for commands, type something like:

export DISPLAY=:0.0
zenity --info --text="Your cornbread is ready"
to pop up a window with a message. But that's too much typing and has the same problem of the small easily-ignored dialogs. I'd really rather have a great big red window that I can't possibly miss.

Surely, I thought, someone has already written a nice egg-timer application! I tried aptitude search timer and found several apps such as gtimer, which is much more complicated than I wanted (you can define named events and choose from a list of ... never mind, I stopped reading there). I tried googling, but didn't have much luck there either (lots of Windows and web apps, no Linux apps or cross-platform scripts).

Clearly just writing the damn thing was going to be easier than finding one. (Why is it that every time I want to do something simple on a computer, I have to write it? I feel so sorry for people who don't program.)

I wanted to do it in python, but what to use for the window that pops up? I've used python-gtk in the past, but I've been meaning to check out TkInter (the gui toolkit that's kinda-sorta part of Python) and this seemed like a nice opportunity since the goal was so simple.

The resulting script: eggtimer. Call it like this:

eggtimer 5 Turn off the water
and in five minutes, it will pop up a huge red window the size of the screen with your message in big letters. (Click it or hit a key to dismiss it.)

First Impressions of TkInter

It was good to have an excuse to try TkInter and compare it with python-gtk. TkInter has been recommended as something normally installed with Python, so the user doesn't have to install anything extra. This is apparently true on Windows (and maybe on Mac), but on Ubuntu it goes the other way: I already had pygtk, because GIMP uses it, but to use TkInter I had to install python-tk.

For developing I found TkInter irritating. Most of the irritation concerned the poor documentation: there are several tutorials demonstrating very basic uses, but not much detailed documentation for answering questions like "What class is the root Tk() window and what methods does it have?" (The best I found -- which never showed up in google, but was referenced from O'Reilly's Programming Python -- was here.) In contrast, python-gtk is very well documented.

Things I couldn't do (or, at least, couldn't figure out how to do, and googling found only postings from other people wanting to do the same thing):

I expect I'll be sticking with pygtk for future projects. It's just too hard figuring things out with no documentation. But it was fun having an excuse to try something new.

Tags: , ,
[ 13:35 Sep 04, 2007    More programming | permalink to this entry ]

Mon, 11 Jun 2007

Find the nearest matching color name

Someone showed up on #gimp today with a color specified as an HTML hex color specifier, and wanted to know how to find the nearest color name.

Easy, right? There have got to be a bazillion pages that do that, plus at least a couple of Linux apps.

But I googled for a while and couldn't find a single one. There are lots of pages that list all the RGB colors, or convert decimal red, green and blue into HTML #nnn hex codes, or offer aesthetic advice about pleasing combinations of colors for themes (including this lovely page on butterfly-inspired color themes, courtesy of Rik) but nothing I could find that gave color names. Apparently there used to be a Linux app that did that, a piece of Gnome 1 called GColorSel, but it's gone now.

I got to thinking (always dangerous!) ... /etc/X11/rgb.txt has a list of color names with their RGB color equivalents. It would be really easy to write something that just read down the list finding the ones closest to the specified color.

Uh-oh ... of course, once that thought occurred to me, I was doomed. Programmer's disease. I had to write it. So I did, and here it is: Find the Nearest Matching Color Name. It checks against both rgb.txt and the much smaller list of 17 CSS color names.

Tags:
[ 22:18 Jun 11, 2007    More programming | permalink to this entry ]

Tue, 29 May 2007

A Culture of Regressions (or, Why I no longer work on Mozilla)

A couple of friends periodically pester me to write about why I stopped contributing to Mozilla after so many years with the project. I've held back, feeling like it's airing dirty laundry in public.

But a discussion on mozilla.dev.planning over the last week, started by Nelson Bolyard, aired it for me: it was their culture of regressions.

I love Mozilla technology. I'm glad it exists, and I still use it for my everyday browsing. But trying to contribute to Mozilla just got too frustrating. I spent more time chasing down and trying to fix other people's breakages than I did working on anything I wanted to work on.

That might be okay, barely, when you're getting paid for it. But when you're volunteering your own time, who wants to spend it fixing code checked in by some other programmer who just can't be bothered to clean up his own mess?

It's the difference between spending a day cleaning your own house ... and spending every day cleaning other people's houses.

Nelson said it eloquently in this exchange:

(Robert Kaiser writes)
As we are open source, everyone can access and test that code, and find and file the regressions, so that they get fixed over time.

(Boris Zbarsky writes)
That last conclusion doesn't necessarily follow. To get them fixed you need someone fixing them.

(Nelson Bolyard writes)
We're very unlikely to get volunteers to spent large amounts of effort, rewriting formerly working code to get it to work again, after it was broken by someone else's checkin. This demotivates developers and drives them away. They think "why should I keep working on this when others can break my code and I must pay for their mistakes?" and "I worked hard to get that working, and now person X has broken it. Let HIM fix it."

This was exactly how I felt, and it's the reason I quit working on Mozilla.

A little later in the thread, Boris Zbarsky reports that the trunk has been so broken with regressions that it's been unusable for him for weeks or months. (When you have someone as committed and sharp as Boris unable to use your software, you know there's something wrong with your project's culture.) He writes: "For example, on my machine (Linux) about one in three SVG testcases in Bugzilla causes trunk Gecko to hang X ..."

Justin Dolske replies, "Oh, Linux," and asks if it's related to turning on Cairo. Boris replies affirmatively. Just another example where a change was checked in that caused serious regressions keeping at least one important contributor from using the browser on a regular basis; yet it's still there and hasn't been backed out. Of course, it's "only Linux".

David Baron appears to take Nelson's concerns seriously, and suggests criteria for closing the tree and making everyone stop work to track down regressions. As he correctly comments, closing the tree is very serious and inefficient, and should be avoided in all but the most serious cases.

But Nelson repeats the real question:

(Nelson Bolyard writes)
Under what circumstances does a Sheriff back out a patch due to functional regressions? From what you wrote above, I gather it's "never". :(

Alas, the thread peters out after that; there's no reply to Nelson's question.

The problem with Mozilla isn't that there are regressions. Mistakes happen. The problem is that regressions never get fixed, because the project's culture encourages regressions. The prevailing attitude is that it's okay to check in changes that break other people's features, as long as your new feature is cool enough or the right people want it. If you break something, well, hey, someone will figure out a fix eventually. Or not. Either way, it's not your problem.

Working on new features is fun, and so is getting the credit for being the one to check them in. Fixing bugs, writing API documentation, extensive testing -- these things aren't fun, they're hard work, and there isn't much glory in them either (you don't get much appreciation or credit for it). So why do them if you don't have to? Let someone else worry about it, as long as the project lets you get away with it!

A project with a culture of responsibility would say that the person who broke something should fix it, and that broken stuff should stay out of the tree. If programmers don't do that themselves just because it's the right thing to do, the project could enforce it: just insist that regression-causing changes that can't be fixed right away be backed out. Fix the regressions out of the tree where they aren't causing problems for other people. Get help from people to test it and to integrate it with those other modules you forgot about the first time around.

Yes, even if it's a change that's needed -- even if it's something a lot of people want. If it's a good change, there will always be time to check it in later.

When it's really working.

Tags:
[ 10:07 May 29, 2007    More programming | permalink to this entry ]

Thu, 22 Mar 2007

Pho 0.9.5 Released

Pho 0.9.5-pre5 has been working nicely since I released it two weeks ago. And meanwhile, I've already started working on the next version. So I've released it as 0.9.5 with no changes (except for version string and some updates to the documentation and debian config files).

I made a .deb on Ubuntu Edgy, but haven't actually tested it yet (anyone who sees problems, please let me know) and I'll try to make a straight Debian package on Sarge sometime soon.

So what's this stuff I've been working on for the next version? Image categorization. I shoot so many photos, and categorizing them by keyword can be a lot of work. Although Pho's "Notes 0 through 9" are helpful for a small number of notes, it's tough keeping track of which note corresponds to which keyword when I'm categorizing a directory full of photos from a trip. The next Pho release (which will have a much shorter release cycle than 0.9.5 did, honest!) will have an optional Keywords dialog where you can type in keywords and associate them with photos. I know there are apps such as f-spot, gthumb and Picasa, but they all seem much more heavyweight than what I need, and Pho only needs a tiny bit of work to get there.

While I'm working on dialogs, I'm also cleaning up modality: Pho dialogs will now stay visible so they can't get lost behind the image, and the question dialog ("Really delete?" or "Do you want to quit?" will be modal.

But that's all coming in the next version. For now, 0.9.5 is the stable version: get it from the Pho page.

Tags:
[ 15:56 Mar 22, 2007    More programming | permalink to this entry ]

Mon, 05 Mar 2007

Pho 0.9.5-pre5

Pho's been static for a long time -- it's been working well enough that I keep forgetting that there were a couple of bugs that need fixing for a 0.9.5 release.

I had some time tonight, so I dug in and fixed the bugs I remembered: some issues with zooming in and out, a bug with aspect ratio when switching out of fullscreen mode, and the fact that Note 0 didn't work.

While I was at it, I added an environment variable, PHO_ARGS, where you can preset your default values. I find that I always want -p (presentation mode), so now I can specify that with PHO_ARGS=p, and use pho -P when I want window borders. I also updated the man page.

I'll test this for a little while and if nobody finds any serious bugs, maybe I can finally release 0.9.5.

Get Pho here.

Tags:
[ 23:06 Mar 05, 2007    More programming | permalink to this entry ]

Wed, 28 Feb 2007

Random Wallpaper

I was talking about desktop backgrounds -- wallpaper -- with some friends the other day, and it occurred to me that it might be fun to have my system choose a random backdrop for me each morning.

Finding backgrounds is no problem: I have plenty of images stored in ~/Backgrounds -- mostly photos I've taken over the years, with a smattering of downloads from sites like the APOD. So all I needed was a way to select one file at random from the directory.

This is Unix, so there's definitely a commandline way to do it, right? Well, surprisingly, I couldn't find an easy way that didn't involve any scripting. Some shells have a random number generator built in ($RANDOM in bash) but you still have to do some math on the result.

Of course, I could have googled, since I'm sure other people have written random-wallpaper scripts ... but what's the fun in that? If it has to be a script, I might as well write my own.

Rather than write a random wallpaper script, I wanted something that could be more generally useful: pick one random line from standard input and print it. Then I could pass it the output of ls -1 $HOME/Backgrounds, and at the same time I'd have a script that I could also use for other purposes, such as choosing a random quotation, or choosing a "flash card" question when studying for an exam.

The obvious approach is to read all of standard input into an array, count the lines, then pick a random number between one and $num_lines and print that array element. It took no time to whip that up in Python and it worked fine. But it's not very efficient -- what if you're choosing a line from a 10Mb file?

Then Sara Falamaki (thanks, Sara!) pointed me to a page with a neat Perl algorithm. It's Perl so it's not easy to read, but the algorithm is cute. You read through the input line by line, keeping track of the line number. For each line, the chance that this line should be the one printed at the end is the reciprocal of the line number: in other words, there's one chance out of $line_number that this line is the one to print.

So if there's only one line, of course you print that line; when you get to the second line, there's one chance out of two that you should switch; on the third, one chance out of three, and so on.

A neat idea, and it doesn't require storing the whole file in memory. In retrospect, I should have thought of it myself: this is basically the same algorithm I used for averaging images in GIMP for my silly Chix Stack Mars project, and I later described the method in the image stacking section of my GIMP book. To average images by stacking them, you give the bottom layer 100% opacity, the second layer 50% opacity, the third 33% opacity, and so on up the stack. Each layer makes an equal contribution to the final result, so what you see is the average of all layers.

The randomline script, which you can inspect here, worked fine, so I hooked it up to accomplish the original problem: setting a randomly chosen desktop background each day. Since I use a lightweight window manager (fvwm) rather than gnome or kde, and I start X manually rather than using gdm, I put this in my .xinitrc:

(xsetbg -fullscreen -border black `find $HOME/Backgrounds -name "*.*" | randomline`) &

So, an overlong article about a relatively trivial but nontheless nifty algorithm. And now I have a new desktop background each day. Today it's something prosaic: mud cracks from Death Valley. Who knows what I'll see tomorrow?

Tags: ,
[ 13:02 Feb 28, 2007    More programming | permalink to this entry ]

Fri, 25 Aug 2006

PyTopo 0.5

Belated release announcement: 0.5b2 of my little map viewer PyTopo has been working well, so I released 0.5 last week with only a few minor changes from the beta. I'm sure I'll immediately find six major bugs -- but hey, that's what point releases are for. I only did betas this time because of the changed configuration file format.

I also made a start on a documentation page for the .pytopo file (though it doesn't really have much that wasn't already written in comments inside the script).

Tags: , , ,
[ 21:10 Aug 25, 2006    More programming | permalink to this entry ]

Mon, 31 Jul 2006

Javascript Warnings Everywhere

I'm working on some little Javascript demos (for a workshop at this summer's Get SET girls' technology camp) so I've had the Javascript Console up for most of my browsing over the last few days. I also have Mozilla's strict Javascript checking on (user_pref("javascript.options.strict", true); in prefs.js or user.js) since I don't want to show the girls code that generates warnings. (Strict mode also reports errors in CSS.)

It's been eye opening how many sites give warnings. You know that nice clean ultra-simple Google search page? One CSS error and one JS warning. But that's peanuts to the pages of errors I see on most sites, and they're not all missing "var" declarations. I have to hit the "Clear" button frequently if I want to be able to see the errors on the pages I'm working on.

And my own sites? Yes, I admit it, I've seen some errors in my own pages too. Though it makes me feel better that there aren't very many of them (mostly CSS problems, not JS). I'm going to keep the JS Console visible more often so I'll see these errors and correct them.

Tags:
[ 23:06 Jul 31, 2006    More programming | permalink to this entry ]

Sat, 03 Jun 2006

Cleaner, More Flexible Python Map Viewing

A few months ago, someone contacted me who was trying to use my PyTopo map display script for a different set of map data, the Topo! National Parks series. We exchanged some email about the format the maps used.

I'd been wanting to make PyTopo more general anyway, and already had some hacky code in my local version to let it use a local geologic map that I'd chopped into segments. So, faced with an Actual User (always a good incentive!), I took the opportunity to clean up the code, use some of Python's support for classes, and introduce several classes of map data.

I called it 0.5 beta 1 since it wasn't well tested. But in the last few days, I had occasion to do some map exploring, cleaned up a few remaining bugs, and implemented a feature which I hadn't gotten around to implementing in the new framework (saving maps to a file).

I think it's ready to use now. I'm going to do some more testing: after visiting the USGS Open House today and watching Jim Lienkaemper's narrated Virtual Tour of the Hayward Fault, I'm all fired up about trying again to find more online geologic map data. But meanwhile, PyTopo is feature complete and has the known bugs fixed. The latest version is on the PyTopo page.

Tags: , , ,
[ 17:25 Jun 03, 2006    More programming | permalink to this entry ]

Mon, 29 May 2006

Aid for Java Victims

Over dinner, I glanced at the cover of the latest Dr. Dobb's (a new article on Ruby on Rails), then switched to BBC World News. The first Beeb headline was Aid flow begins for Java victims.

I guess I was a little distracted from dinner preparations ... my first thought was "Are they going to give them all copies of Ruby and Rails?"

Then, of course, I remembered the earthquake. Oh, right, those Java victims!

(Not to make light of the situation there, which sounds grim. And just as I was writing this, I got email from the USGS Earthquake Notification Service reporting another aftershock in Indonesia, this one magnitude 5.6. I hope it doesn't make matters worse.)

Tags:
[ 21:05 May 29, 2006    More programming | permalink to this entry ]

Fri, 17 Mar 2006

MySQL Losing its Socket!

This morning I was all ready to continue working on an ongoing web project when I discovered that mysql wasn't running.

That's funny, it was running fine yesterday! I tried /etc/init.d/mysql start, but it failed. The only error message was, "Please take a look at the syslog."

So I hied myself over to /var/log, to discover that mysql.log and mysql.err were both there, but empty.

Some poking around /etc/mysql/my.cnf revealed that logging is commented out by default, because: "# Be aware that this log type is a performance killer."

I uncommented logging and tried again, but /var/log/mysql.err remained blank, and all that was in mysql.log was three lines related basically giving its runtime arguments and the name of the socket file.

Back to the drawing board. I was well aware that I had changed the mysql settings yesterday. See, mysqld on Ubuntu likes to create its socket as /var/run/mysqld/mysqld.sock, but other apps, like Ruby, all expect to find it in /tmp/mysql.sock. It's easy enough to change Ruby's expectations. But then I found out that although the cmdline client mysql also expects the socket in /var/run/mysqld, it depends on something called mysqladmin that wants the socket in /tmp. (I may have those two reversed. No matter: the point is that you can't use the client to talk to the database because it and the program it depends on disagree about the name of the socket. This is probably a Dapper bug.)

Okay, so I had to pick one. I decided that /tmp/mysql.sock was easier to remember and more standard with non-Debian setups. I knew where to change it in the server (/etc/mysql/my.cnf is there and well commented) but the mysql client doesn't use that, and it took some googling and help from clueful friends to find out that what it wanted was a new file called /etc/my.cnf (how's that for a nice clear configuration file name?) containing one line:

socket          = /tmp/mysql.sock

That done, mysql started and ran and everything worked. Woo!

Except that it didn't the following morning after a reboot, and didn't give any error messages as to why.

Off I went on a merry chase across init files: /etc/init.d/mysql calls /etc/mysql/debian-start (which made me realize that debian has added yet another config file, debian.cnf, which has yet another copy of the line specifying the socket filename) which calls /usr/share/mysql/debian-start.inc.sh as well as calling various other programs. But those were all red herrings: the trick to debugging the problem was to run mysqld directly (not via /etc/init.d/mysql start: it actually does print error messages, but they were being hidden by using the init.d script.

The real problem turned out to be that I had changed the location of the socket file, but not the pid file, in /etc/mysql/my.cnf, which was also located by default in /var/run/mysqld. Apparently that directory is created dynamically at each boot, and it isn't created unless it's needed for the socket file (whether the pid file needs it doesn't matter). So since I'd moved the socket file to /tmp, /var/run/mysqld wasn't created, mysqld couldn't create its pid file and it bailed. Solution: edit my.cnf to use /tmp for the pid file.

Tags:
[ 12:29 Mar 17, 2006    More programming | permalink to this entry ]

Mon, 13 Mar 2006

Ruby on Rails on Ubuntu

Back when I laboriously installed Ruby and Rails on Ubuntu "Hoary Hedgehog" (which involved basically ignoring all the Ubuntu packages and building everything, including Ruby itself, from source), I was cheered by the notes in Ubuntu's forums and bugzilla indicating that as of the next release ("Breezy Badger") all the right versions would be there, and all this source compilation would no longer be necessary.

I didn't get around to trying it until today. Breezy and its successor "Dapper Drake" do indeed have a rails package as well as a Ruby package, and I happily installed them. All looked great -- until I actually tried to use them on a real-world application. It turns out that the Ruby and Rails packages don't include gems, Ruby's package manager (similar to the CPAN system familiar to Perl programmers). And gems is required for doing anything useful in Rails.

Drat! After several false starts, I eventually found the instructions on this page. Except that installs way more than seems necessary for what I need to do, and if you copy/paste lines from that page you may end up with a bunch of packages you don't want, like an out of date version of mysql.

So here are simplified instructions for using Ruby on Rails on Ubuntu Breezy or Dapper.

As yourself:

wget http://rubyforge.org/frs/download.php/5207/rubygems-0.8.11.tgz
tar zxvf rubygems-0.8.11.tgz
As root:
cd rubygems-0.8.11
ruby setup.rb
gem install rubygems-update
gem install rails

Say yes to all dependency questions during the gem install of rails. Add your web server and database of choice (you probably already have them installed, anyway) and you should be good to go.

You may note that the page I referenced tells you to install two versions of rails: the Ubuntu package plus the one from gems. At least on Dapper, you don't need both. Installing rails pulls in the packages:
irb irb1.8 libpgsql-ruby1.8 libreadline-ruby1.8 libredcloth-ruby1.8 libruby1.8 rake rdoc rdoc1.8 ruby ruby1.8
I haven't experimented with which of these packages are and are not needed. If you run into problems, some set of packages from this list may solve them.

Update: it seems that none of these are required. Many of them are dummy packages anyway, which contain no files related to the actual package name. For instance, the rake package contains, not rake, but a single bash completion file related to rake. So you should be fine ignoring all of them, installing just Ruby and nothing else.

(I filed bug 34840 requesting that Ubuntu ship a usable version of Rails, since it didn't seem to be filed already.)

Tags:
[ 21:56 Mar 13, 2006    More programming | permalink to this entry ]

Fri, 30 Sep 2005

Set C Styles Automatically in Emacs

I just figured out a nifty emacs trick: how to set C styles automatically based on the location of the file I'm editing. That way, emacs will autoindent with the proper style whether I'm editing my own code, GIMP code, Linux kernel code, etc. assuming that I keep the source for these projects in predictable places.

First, define a derived mode for each style you might want to use. Each minor mode inherits from c-mode, but adds a c-set-style call. (Obviously, this assumes that the styles you want to use are already defined.)

(define-derived-mode gnu-c-mode c-mode "GNU C mode"
  (c-set-style "gnu"))
(define-derived-mode linux-c-mode c-mode "GNU C mode"
  (c-set-style "linux"))

Then add entries to your auto-mode-alist for each directory you want to treat specially:

(setq auto-mode-alist
[ . . . ]
      (cons '("gimp.*/" . gnu-c-mode)
      (cons '("linux-.*/" . linux-c-mode)
            auto-mode-alist) ) )

Your normal default style will still work for any files not matching the patterns specified in auto-mode-alist.

It might be possible to skip the step of defining derived modes by switching on the file's pathname in the C mode hook, but I've never figured out how to get the pathname of the file being loaded in any reliable way.

Tags:
[ 12:42 Sep 30, 2005    More programming | permalink to this entry ]

Tue, 26 Jul 2005

New Pho

Pho 0.9.5-pre4 seems to be working pretty well and fixes a couple of bugs in pre3, so I posted a tarball. I really need to quit this pre- stuff and just release 0.9.5. Soon, really!

Tags:
[ 11:51 Jul 26, 2005    More programming | permalink to this entry ]

Tue, 21 Jun 2005

A Fast Volume Control App

I updated my Debian sid system yesterday, and discovered today that gnome-volume-control has changed their UI yet again. Now the window comes up with two tabs, Playback and Capture; the default tab, Playback, has only one slider in it, PCM, and all the important sliders, like Volume, are under Capture. (I'm told this is some interaction with how ALSA sees my sound chip.)

That's just silly. I've never liked the app anyway -- it takes forever to come up, so I end up missing too much of any clip that starts out quiet. All I need is a simple, fast window with a single slider controlling master volume. But nothing like that seems to exist, except panel applets that are tied to the panels of particular window managers.

So I wrote one, in PyGTK. vol is a simple script which shows a slider, and calls aumix under the hood to get and set the volume. It's horizontal by default; vol -h gives a vertical slider.

Aside: it's somewhat amazing that Python has no direct way to read an integer out of a string containing more than just that integer: for example, to read 70 out of "70,". I had to write a function to handle that. It's such a terrific no-nonsense language most of the time, yet so bad at a few things. (And when I asked about a general solution in the python channel at [large IRC network], I got a bunch of replies like "use int(str[0:2])" and "use int(str[0:-1])". Shock and bafflement ensued when I pointed out that 5, 100, and -27 are all integers too and wouldn't be handled by those approaches.)

Tags: , , ,
[ 14:54 Jun 21, 2005    More programming | permalink to this entry ]

Wed, 13 Apr 2005

PyTopo 0.3

I needed to print some maps for one of my geology class field trips, so I added a "save current map" key to PyTopo (which saves to .gif, and then I print it with gimp-print). It calls montage from Image Magick.

Get yer PyTopo 0.3 here.

Tags: , ,
[ 16:56 Apr 13, 2005    More programming | permalink to this entry ]

Sat, 09 Apr 2005

Python Expose vs. Focus

A few days ago, I mentioned my woes regarding Python sending spurious expose events every time the drawing area gains or loses focus.

Since then, I've spoken with several gtk people, and investigated several workarounds, which I'm writing up here for the benefit of anyone else trying to solve this problem.

First, "it's a feature". What's happening is that the default focus in and out handlers for the drawing area (or perhaps its parent class) assume that any widget which gains keyboard focus needs to redraw its entire window (presumably because it's locate-highlighting and therefore changing color everywhere?) to indicate the focus change. Rather than let the widget decide that on its own, the focus handler forces the issue via this expose event. This may be a bad decision, and it doesn't agree with the gtk or pygtk documentation for what an expose event means, but it's been that way for long enough that I'm told it's unlikely to be changed now (people may be depending on the current behavior).

Especially if there are workarounds -- and there are.

I wrote that this happened only in pygtk and not C gtk, but I was wrong. The spurious expose events are only passed if the CAN_FOCUS flag is set. My C gtk test snippet did not need CAN_FOCUS, because the program from which it was taken, pho, already implements the simplest workaround: put the key-press handler on the window, rather than the drawing area. Window apparently does not have the focus/expose misbehavior.

I worry about this approach, though, because if there are any other UI elements in the window which need to respond to key events, they will never get the chance. I'd rather keep the events on the drawing area.

And that becomes possible by overriding the drawing area's default focus in/out handlers. Simply write a no-op handler which returns TRUE, and set it as the handler for both focus-in and focus-out. This is the solution I've taken (and I may change pho to do the same thing, though it's unlikely ever to be a problem in pho).

In C, there's a third workaround: query the default focus handlers, and disconnect() them. That is a little more efficient (you aren't calling your nop routines all the time) but it doesn't seem to be possible from pygtk: pygtk offers disconnect(), but there's no way to locate the default handlers in order to disconnect them.

But there's a fourth workaround which might work even in pygtk: derive a class from drawing area, and set the focus in and out handlers to null. I haven't actually tried this yet, but it may be the best approach for an app big enough that it needs its own UI classes.

One other thing: it was suggested that I should try using AccelGroups for my key bindings, instead of a key-press handler, and then I could even make the bindings user-configurable. Sounded great! AccelGroups turn out to be very easy to use, and a nice feature. But they also turn out to have undocumented limitations on what can and can't be an accelerator. In particular, the arrow keys can't be accelerators; which makes AccelGroup accelerators less than useful for a widget or app that needs to handle user-initiated scrolling or movement. Too bad!

Tags: , , ,
[ 20:52 Apr 09, 2005    More programming | permalink to this entry ]

Wed, 06 Apr 2005

PyTopo is usable; pygtk is inefficient

While on vacation, I couldn't resist tweaking pytopo so that I could use it to explore some of the areas we were visiting.

It seems fairly usable now. You can scroll around, zoom in and out to change between the two different map series, and get the coordinates of a particular location by clicking. I celebrated by making a page for it, with a silly tux-peering-over-map icon.

One annoyance: it repaints every time it gets a focus in or out, which means, for people like me who use mouse focus, that it repaints twice for each time the mouse moves over the window. This isn't visible, but it would drag the CPU down a bit on a slow machine (which matters since mapping programs are particularly useful on laptops and handhelds).

It turns out this is a pygtk problem: any pygtk drawing area window gets spurious Expose events every time the focus changes (whether or not you've asked to track focus events), and it reports that the whole window needs to be repainted, and doesn't seem to be distinguishable in any way from a real Expose event. The regular gtk libraries (called from C) don't do this, nor do Xlib C programs; only pygtk.

I filed bug 172842 on pygtk; perhaps someone will come up with a workaround, though the couple of pygtk developers I found on #pygtk couldn't think of one (and said I shouldn't worry about it since most people don't use pointer focus ... sigh).

Tags: , , ,
[ 16:26 Apr 06, 2005    More programming | permalink to this entry ]

Sun, 27 Mar 2005

Python GTK Topographic Map Program

I couldn't stop myself -- I wrote up a little topo map viewer in PyGTK, so I can move around with arrow keys or by clicking near the edges. It makes it a lot easier to navigate the map directory if I don't know the exact starting coordinates.

It's called pytopo, and it's in the same place as my earlier two topo scripts.

I think CoordsToFilename has some bugs; the data CD also has some holes, and some directories don't seem to exist in the expected place. I haven't figured that out yet.

Tags: , , ,
[ 17:53 Mar 27, 2005    More programming | permalink to this entry ]

Topographic Maps for Linux

I've long wished for something like those topographic map packages I keep seeing in stores. The USGS (US Geological Survey) sells digitized versions of their maps, but there's a hefty setup fee for setting up an order, so it's only reasonable when buying large collections all at once.

There are various Linux mapping applications which do things like download squillions of small map sections from online mapping sites, but they're all highly GPS oriented and I haven't had much luck getting them to work without one. I don't (yet?) have a GPS; but even if I had one, I usually want to make maps for places I've been or might go, not for where I am right now. (I don't generally carry a laptop along on hikes!)

The Topo! map/software packages sold in camping/hiking stores (sometimes under the aegis of National Geographic are very reasonably priced. But of course, the software is written for Windows (and maybe also Mac), not much help to Linux users, and the box gives no indication of the format of the data. Googling is no help; it seems no Linux user has ever tried buying one of these packages to see what's inside. The employees at my local outdoor equipment store (Mel Cotton's) were very nice without knowing the answer, and offered the sensible suggestion of calling the phone number on the box, which turns out to be a small local company, "Wildflower Productions", located in San Francisco.

Calling Wildflower, alas, results in an all too familiar runaround: a touchtone menu tree where no path results in the possibility of contact with a human. Sometimes I wonder why companies bother to list a phone number at all, when they obviously have no intention of letting anyone call in.

Concluding that the only way to find out was to buy one, I did so. A worthwhile experiment, as it turned out! The maps inside are simple GIF files, digitized from the USGS 7.5-minute series and, wonder of wonders, also from the discontinued but still useful 15-minute series. Each directory contains GIF files covering the area of one 7.5 minute map, in small .75-minute square pieces, including pieces of the 15-minute map covering the same area.

A few minutes of hacking with python and Image Magick resulted in a script to stitch together all images in one directory to make one full USGS 7.5 minute map; after a few hours of hacking, I can stitch a map of arbitrary size given start and end longitude and latitude. My initial scripts, such as they are.

Of course, I don't yet have nicities like a key, or an interactive scrolling window, or interpretation of the USGS digital elevation data. I expect I have more work to do. But for now, just being able to generate and print maps for a specific area is a huge boon, especially with all the mapping we're doing in Field Geology class. GIMP's "measure" tool will come in handy for measuring distances and angles!

Tags: , , ,
[ 11:13 Mar 27, 2005    More programming | permalink to this entry ]

Wed, 16 Mar 2005

Kitfox: Firefox for People Who Liked the Mozilla Browser

Debate rages on the mozilla-seamonkey list since the Mozilla Foundation announced that there would be no 1.8 release of the Mozilla browser (also called "the suite", or by its code name, "seamonkey"). Suite users are frustrated at lack of notice: anyone who was paying attention knew that seamonkey was going to be dropped eventually, but everyone expected at least a 1.8 final release. Mozilla.org is frustrated because they wish suite users would quit whining and switch to Firefox. Various people are slinging flames and insults, while a few try to mediate with logic and sense. There's a volunteer effort ramping up to continue support for the suite, but no plans for what to do about fixing all the regressions. Go read the list if you want all the gory details.

Anyway, the writing on the wall (and on the newsgroup) is clear: if you want a browser with continuing support from mozilla.org, Firefox is your only choice. Unfortunately for Linux users, firefox is designed by and for Windows users, copying Internet Explorer's user interface and dropping support for a number of nice features which the old mozilla browser offered.

I've decided that the best way to get a usable browser is to take firefox and put back the mozilla features that I miss. Mostly these are easy user interface tweaks. I pulled a tree last week and had most of the items that were blocking me addressed in a few hours. Building wasn't entirely straightforward: the build page doesn't make all the options clear, like the fact that xft and freetype are both enabled by default, so one of them has to be explicitly disabled. Updating the tree turns out to be a bit problematic: firefox' build dependencies turn out to be dicy, so sometimes changing a single .xul file causes the entire tree to rebuild, while other times an update builds a few files and the resulting build fails to run, and requires a clobber and a rebuild. Still, those problems are relatively minor.

So far, I have fixes for these bugs:

Next up: try to figure out why firefox takes so much longer than mozilla to start up. Fortunately, once it's up, it seems just as fast at browsing, but startup takes forever, and firefox doesn't even offer a splash option to tell me that something is happening.

Here is my patch, in case anyone else is bothered by these issues.

Perhaps this could be built as an extension. Some day I'll look into that. Certainly the current set of patches could be implemented as a script which exploded, edited, and re-packed the .jar archives in a firefox binary build, since the patch touches no C++ code as yet.

I'm calling my firefox-derived browser "Kitfox".

Tags:
[ 11:03 Mar 16, 2005    More programming | permalink to this entry ]

Tue, 09 Nov 2004

New Crikey!

Crikey 0.5 is out, with some changes contributed by Efraim Feinstein -- it can read from stdin now, and has a debug flag. Reading from stdin means you can generate multi-line text now.

It's so cool when people send patches to my programs! Especially when they're nice clean code implementing useful features. Thanks, Efraim!

Tags:
[ 21:39 Nov 09, 2004    More programming | permalink to this entry ]

Sun, 10 Oct 2004

Pho 0.9.5-pre3: evil Metacity window sizing

It turns out that the problem with pho windows not resizing in metacity is this: when metacity sees a window that's slightly larger (in either dimension) than the screen size, it unilaterally makes that window maximized, and thereafter refuses any request from the app to resize the window smaller.

Mandatory maximize might actually be useful in some circumstances (anyone who's ever tried to run on an 800x600 laptop has doubtless seen dialogs which don't fit on the screen) but the subsequent refusal to resize makes little sense, and causes bustage in programs which work fine under other window managers.

A workaround is for pho to unmaximize before any window resize. This would be a bummer with an app where the user might click the maximize button manually; with pho, that's unlikely (I hope) because anyone who wants to run maximized is better off running in fullscreen/presentation mode (which now finally sets its background to black, hooray).

Get'cher Pho 0.9.5-pre3 here.

Tags:
[ 18:21 Oct 10, 2004    More programming | permalink to this entry ]

Fri, 01 Oct 2004

New showpix script now uses PHP

I'd been meaning for ages to write a PHP version of my showpix.cgi Perl script, to show images without needing a separate .html file generated for each image. I finally did it this morning, and it was much easier than I expected, and seems to run a lot faster than the perl CGI (not surprising, since PHP is cached in our web server and perl isn't; so this should be more scaleable and less load on the server).

The hardest part was writing the Python script to generate a new showpix.php for a directory of images, and that only because of all the escaping of quotes that needed to be done when telling python to print a line that tells php to print a line to serve up over http ...

Anyway, I've converted the Flume Trail images to use the new PHP stuff, and I've updated the page for the Imagebatch scripts to include PHP ability.

Tags:
[ 14:40 Oct 01, 2004    More programming | permalink to this entry ]

Thu, 30 Sep 2004

Pho 0.9.5-pre2

I finally gathered together some of the pending fixes for Pho, so it keeps better track of where it is in the linked list when deleting or jumping around. No progress on the focus handling, though. I sure would like to solve that before 0.9.5 final.

Tags:
[ 21:42 Sep 30, 2004    More programming | permalink to this entry ]

Tue, 28 Sep 2004

ADA doesn't apply to the web?!

The 11th Circuit Court of Appeals ruled last Friday that the Americans with Disabilities Act does not apply to the web. In other words, web designers are not required to make their pages accessible to readers with disabilities.

That seems very odd. It's required of software -- several lawsuits have been filed (and settled) related to inaccessible software. Accessibility is required universally for web sites in the EU, and I thought it was required of US government sites as well. It's a shame the court felt that it wasn't important for web sites in general.

Accessibility helps everybody, not just people with disabilities (even setting aside the fact that most of us will eventually experience some impairment, if we live long enough). Accessible web sites are usually easier to read and navigate, and translate more easily to offline readers (such as Sitescooper) and PDA readers (such as Opera and Plucker).

The decision was "largely on procedural grounds" and the court suggested that the decision could be revisited in a future case. I hope that case comes soon, before US web designers conclude there's no point in designing for accessibility.

W3C Web Content Accessibility Guidelines 2.0.

Tags:
[ 12:05 Sep 28, 2004    More programming | permalink to this entry ]

Sun, 25 Jul 2004

XShapeCombineMask generates an Expose loop

I tried moonroot on blackbird today, under icewm for the first time. It went into an infinite expose/redraw loop. It turns out that XShapeCombineMask (the call that sets the shaped window's shape mask) generates an extra Expose, which of course happens asynchronously so disabling expose handling in the draw routine doesn't help.

What does help is maintaining a static variable to ensure that it only shapes the window the first time, and not on subsequent draws.

I also tweaked sonypid.c a bit -- 2.4.25 is generating two jogdial-release events whenever the machine resumes from bios suspend. But there's no jogdial-press event corresponding, so I fixed sonypid to ignore jogdial release unless there's already been a jogdial press (again, maintaining a static variable; I already had one so that it doesn't trigger a release after an UP+PRESSED or DOWN+PRESSED event, so I just had to tweak that code a little). That should eliminate that annoying paste that was happening every time I resumed from suspend.

Wish sonypi would quit changing, though I shouldn't complain since it's also good to see that it's still being worked on.

Tags:
[ 22:55 Jul 25, 2004    More programming | permalink to this entry ]

Wed, 14 Jul 2004

Pho Makefile now builds under either gtk1 or gtk2

I fixed the pho Makefile to build under either gtk1 or gtk2, which required some grody bash code. I wish I knew of a better way to do that. Aren't massive API changes fun? Whee!

Patti told me about a park I didn't know about: the Ulistac Natural Area. I drove by it today after Toastmasters. It's a very small linear park along a levee, with one wooded area and a longer and skinnier open area. I didn't go in -- the combination of migraine and hot sun sapped my interest in a walk.

Tags:
[ 19:00 Jul 14, 2004    More programming | permalink to this entry ]

Sun, 11 Jul 2004

Rootmoon

I finally whipped up an app I've been thinking about for a while: a little moon drawn with a shaped window, so you can put it on your root like that OS X moondock applet that Dave uses. Partly it was an excuse to play with shaped windows, which I hadn't used before, and partly it's that I hate seeing stuff that OS X can do that Linux can't.

I put it up (currently temp-named "moonroot" on my software page.

I also caught some nice shots of a hummer at the feeder (through the screen) and added them to my Hummingbird Photo page. A fairly productive day, really, including a nice hike.

Tags:
[ 19:00 Jul 11, 2004    More programming | permalink to this entry ]

Thu, 08 Jul 2004

Pho and window managers

I worked some more on pho yesterday, trying to fix some of the problems Dave has been seeing in other window managers, particularly Metacity. I got rid of most of the problems, but metacity still asks him to place and size the initial window. I have no idea why; I'm specifying initial window size, and no other program asks him that. I also haven't solved the problem of the window getting focus after it resizes out from under the mouse cursor (in lots of window managers, such as openbox). Typing that, I just got a brainstorm: maybe it's a race condition, that the focus loss doesn't happen until after expose, but the "ask for focus" happens before the resize has propogated through the X server. Must try!

Tags:
[ 14:00 Jul 08, 2004    More programming | permalink to this entry ]

Wed, 07 Jul 2004

Rewrote Pho

I finally got around to rewriting pho! The code is much cleaner now -- images are stored as structs in a linked list, no more motley collections of weird global arrays. Code is factored better, and it builds for gtk2. But the main motivation for rewriting it was to have it make a new window for each new or resized image, replacing the old window, to solve a bunch of window manager bugs I keep hitting:

I handed the new pho to Dave for testing, and he hates the "new window each time" model; it takes too long in the window managers he runs. He says he wasn't that bothered by the repainting/resizing problems.

Fortunately, the rewrite factored the code so that it should be easy to provide both options (that was the plan anyway), isolated in the NewWindow routine. So I'll put back the "resize and reposition existing window" code, as a switchable option, and maybe try to grab focus to solve the pointer focus issues that have been plaguing me. I don't know what to do about the window manager resize/repaint issues; more research is required.

Tags:
[ 22:00 Jul 07, 2004    More programming | permalink to this entry ]