George V. Reilly's Technical Blog

April 2009 - Posts

Mobile Device Browser File
Mobile Device Browser File

In the late 90s when I worked on the classic Active Server Pages dev team, I tried to convince one of the Program Managers that we should make regular updates to browscap.ini, the file that described browser capabilities. He wanted no part of it.

I was pleasantly surprised to learn via Hanselminutes that Microsoft has stepped up to its responsibilities and is now shipping the Mobile Device Browser File on CodePlex. Over 400 mobile devices are defined, with 67 distinct capabilities.

The Hanselminutes podcast is an interesting discussion of the Mobile Web and designing a different experience for mobile browsers. There's more to it than the small screen. You want to think about the scenarios in which the site is likely to be used. The user is probably traveling: give directions. Make phone numbers dialable by using the tel: scheme. Think about server round trips.

Ubuntu Netbook Remix 9.04
Ubuntu Netbook Remix

I spent much of today playing around with the brand-new Jaunty/9.04 release of the Ubuntu Netbook Remix on my Eee 1000H netbook. Previously I had run the Hardy/8.04 version of Ubuntu Eee on this system. I had never bothered to update to Intrepid/8.10, but now that UNR is fully supported by Canonical, I thought it was time to try it out.

I downloaded the UNR image last night onto my Mac, and transferred the image to a 1GB USB stick this morning. (The Mac instructions required a little tweaking.)

I spent some time running the Live Image first, before clean installing. Everything worked seamlessly except the microphone. WiFi worked, the webcam worked, sound playback worked, the touchpad was configured in a sane way. All of these were problems for me when I first installed Ubuntu Eee. That they worked now was not too surprising, since the Asus Eee 1000 is a Tier 1 supported system, but it's nice to get the confirmation.

One of the first things to strike me about the Live Image was how nice the fonts looked. I'm sensitive to typography and the default font hinting settings on previous versions of Ubuntu have always looked like crap: spindly and awkward. I found it hard to take seriously an operating system that looked so unprofessional. The Jaunty font hinting yields thicker letters, which look a lot more like the Mac's shape-preserving font rendering, though not as good. The main exception, oddly enough, is the font used in the netbook-launcher, which looks jagged.

I went ahead and installed Jaunty. The installer offered me an option to install Jaunty side-by-side with the existing operating systems, Ubuntu Eee 8.04 and Windows XP. I wanted to overwrite the existing Ubuntu partition and I had to jump through several hoops to make that happen. The partition editor is pretty and an improvement over GParted. The timezone picker is also very slick, with a clickable world map.

Partitioning aside, the installation was quick and painless. JPierre has a useful guide to some issues that he ran into.

I've spent most of the afternoon and evening installing various applications that I care about. Sleep and hibernate just work now. Sleep worked before but there were always some obnoxious errors when going to sleep.

As a hardcore Vim user, I use keyboard shortcuts a lot. Alt+Tab (or Apple+Tab) is my primary method for switching between applications on Windows, Linux, and Mac. I had never found a keyboard shortcut for switching back to the netbook-launcher: I'd always have to click the Ubuntu logo in the top-left corner of the desktop. Buried in the Keyboard Shortcuts Preferences, I finally found Ctrl+Alt+Tab, which shows a popup, and Ctrl+Alt+Escape, which switches immediately.

Other random notes:

  • I had to rediscover ntfs-config to automount my NTFS drives.
  • Useful apps like Skype can be installed from the Medibuntu repository.
  • It's necessary to run dropbox start -i before Dropbox will download the real daemon and actually start running.

I have a Linux machine at work that runs Kubuntu. I kicked off the upgrade from Intrepid to Jaunty yesterday before I left. I'll find out on Monday how well that worked.

Sprints
Sprints

Scrum and Agile revolve around sprints. At my previous employer, I spent two years working in one-week sprints. At my current job, I've spent another two years working in four-week sprints.

Each has their own rhythm. We ran the one-week sprint from Wednesday to the following Tuesday. Wednesday morning, we'd demo the previous week's work and we'd plan, drawing up a series of task cards, measured in hours. With a one-week horizon, you couldn't go very far off track. You can't get a huge amount done in a week either. You need to have a bigger picture in mind that transcends several weeks. We released every couple of months.

On the first Monday of the four-week sprint, we review the sprint backlog and break down the features into finer grained tasks. In the fourth week, we look at the product backlog and prioritize the features to go on to the next sprint's backlog. Features are measured as Small (1 week), Medium (2 weeks), or Large (4 weeks). On the fourth Friday, we have demos. We also estimate our velocity for the next sprint, based on how much we delivered in the current sprint. This determines how much we sign up for at the beginning of the next sprint.

With the four-week sprint, you build up momentum and you have enough time to deliver significant functionality. The planning is harder though.

I prefer the rhythm of a four-week sprint, but I could go back to the shorter one.

Today is the last day of a four-week sprint. We got a lot done, though it came down to the wire yesterday.

Posted: Apr 24 2009, 11:55 PM by george_v_reilly | with 1 comment(s)
Filed under:
CSS in Email
CSS support in web email clients

I spent part of a day last week fighting with CSS for an email template. CSS support is poor in both desktop and web clients, and much worse than in current browsers.

Gmail, for example, does not support <style> in either the head or the body of HTML email. You have to explicitly set style attributes on individual nodes. You might as well be using <font> tags!

You can't assume that images will be downloaded, so the mail has to make sense without them. And forget iframes.

CampaignMonitor seems to have the definitive guide to CSS support.

Posted: Apr 24 2009, 11:19 PM by george_v_reilly | with no comments
Filed under:
InstantShot!
InstantShot!

Many of the screenshots that show up on my blog were captured with ImageWell, a little Mac app with resizing, uploading, and rudimentary image editing functionality. It used to be freeware. Now it costs $20 after the trial period runs out.

InstantShot! is a menu bar app that does a good job of taking screenshots, but that's all it does.

ChocoFlop, which I've only just discovered, seems like the best of the free image editors for the Mac. The rest are pretty bad. Nothing as good as Paint.NET on Windows.

GIMP on OS X has finally become more or less usable, but that's heavyweight.

Posted: Apr 24 2009, 11:17 PM by george_v_reilly | with no comments
Filed under:
Python String Formatting
string formatting

Python has long had a string interpolation operator, %.

Python 2.6 and 3.0 introduced a new, richer set of string formatting operations. See PEP 3101 for the rationale.

One trick that I liked with the old way of formatting was to put the locals() dictionary or self.__dict__ on the right-hand side

>>> def stuff(a, b):
...  c = a+b; d = a-b
...  return "%(a)s, %(b)s, %(c)s, %(d)s" % locals()
...
>>> stuff(3, 17)
'3, 17, 20, -14'

It took me a few minutes to figure out how to do the equivalent with string.format: use the ** syntax to unpack the dict into kwargs.

>>> class Person(object):
...  def __init__(self, name, age):
...   self.name = name
...   self.age = age
...  def old(self):
...   return "name=%(name)s, age=%(age)d" % self.__dict__
...  def new(self):
...   return "name={name}, age={age}".format(**self.__dict__)
...  def dict(self):
...   return "name={0[name]}, age={0[age]}".format(self.__dict__)
...
>>> gb = Person('George Burns', 100)
>>> gb.old()
'name=George Burns, age=100'
>>> gb.new()
'name=George Burns, age=100'
>>> gb.dict()
'name=George Burns, age=100'

The getitem variant ({0[name]}) might be slightly more efficient, since the dict does not need to be flattened, but I doubt it makes a perceptible difference in practice.

Posted: Apr 24 2009, 11:13 PM by george_v_reilly | with 1 comment(s)
Filed under:
De-partitioning a Mac disk
Disk Utility after de-partitioning

I wrote yesterday about NTFS-3G because I was backing my MacBook to an external NTFS drive. I was backing up because I wanted to de-partition my Mac.

When I upgraded my MacBook to a bigger drive, more RAM, and OS X 10.5, I partitioned the drive. I created two 25GB partitions with the intention of putting Windows and Linux on them with BootCamp. It turns out that BootCamp doesn't like that. It wants the system drive to have only one partition, which it would shrink. I never bothered to go any further.

The disk has been filling up recently and I wanted the extra space back, to extend my primary HFS+ partition by 50GB. I found a guide to nondestructively resizing volumes with the command-line diskutil tool.

With some trepidation, I set out to reclaim the end of my drive. Happily, it turned out to be both quick and painless.

Here's the old disk layout:

georger@georger-macbook:~$ diskutil list
/dev/disk0
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *298.1 Gi disk0
1: EFI 200.0 Mi disk0s1
2: Apple_HFS GeorgeR Mac 250.0 Gi disk0s2
3: Microsoft Basic Data WINDOWS 25.0 Gi disk0s3
4: Microsoft Basic Data LINUX 22.8 Gi disk0s4

First, I merged the two FAT32 partitions into one HFS+ partition:

georger@georger-macbook:~$ sudo diskutil mergePartitions \
"Journaled HFS+" End disk0s3 disk0s4
The chosen disk does not support resizing, do you wish to format instead? (y/N) y
Merging partitions into a new partition
Start partition: disk0s3 WINDOWS
Finish partition: disk0s4 LINUX
Started erase after partitioning on disk disk0s3
Erasing
Mounting disk
[ + 0%..10%..20%..30%..40%..50%..60%..70%..80%..90%..100% ]
Finished erase after partitioning on disk disk0s3 End
/dev/disk0
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *298.1 Gi disk0
1: EFI 200.0 Mi disk0s1
2: Apple_HFS GeorgeR Mac 250.0 Gi disk0s2
3: Apple_HFS End 47.6 Gi disk0s3

Then, I merged the two HFS+ partitions:

georger@georger-macbook:~$ sudo diskutil mergePartitions \
"Journaled HFS+" End disk0s2 disk0s3
Merging partitions into a new partition
Start partition: disk0s2 GeorgeR Mac
Finish partition: disk0s3 End
Attempting resize
Changing filesystem size on disk 'disk0s2'...
Attempting to change filesystem size from 268435456000 to 319728959488 bytes
/dev/disk0
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *298.1 Gi disk0
1: EFI 200.0 Mi disk0s1
2: Apple_HFS GeorgeR Mac 297.8 Gi disk0s2

And everything is good:

georger@georger-macbook:~$ df
Filesystem 512-blocks Used Avail Capacity Mounted on
/dev/disk0s2 624470624 508168048 115790576 81% /

Disclaimer: back your disk up and read the diskutil man page very carefully before using: one misstep could ruin the contents of your disk.

Posted: Apr 11 2009, 11:34 PM by george_v_reilly | with 16 comment(s)
Filed under:
NTFS-3G: the universal filesystem
NTFS-3G

After I started running Linux and then Mac OS X, in addition to Windows, I started on a quest to find the universal filesystem. I had multiboot systems and external drives where I wanted to to be able to read and write disks under multiple operating systems.

The obvious choice is FAT32, the ubiquitous, lowest-common denominator filesystem. FAT32 is supported out-of-the-box by all major operating systems, digital cameras, and PDAs, so that's a huge advantage. FAT32 also has major shortcomings:

  • Maximum file size is 4GB. I have ISOs, MPEGs, and other large files exceeding this limit.
  • Fragmentation happens too easily.
  • Timestamps: accurate only to 2-second resolution. No notion of timezones or UTC.
  • Journaling: none. Preferred for robustness.
  • ACLs or Permissions. Nothing beyond R/W.

I experimented with ext3 (and its non-journaling sibling, ext2) on Windows and later on the Mac. On Windows, ext2fs works well and I used it happily for several months on a machine dualbooting XP and Ubuntu. It did not work well with Vista initially, though that seems to have been fixed since.

My experiences on the Mac were bad: ext2fsx caused some kernel panics, which was enough for me to abandon it.

There was no free solution for reading and writing Mac HFS+ disks under Linux and Windows the last time that I checked.

Both Linux and Macs natively support mounting NTFS disks read-only. The NTFS-3G project allows Linux to write to NTFS disks, and Mac NTFS-3G does likewise for Macs. I've never had a problem with NTFS-3G and it's worked flawlessly under Linux and Mac for me.

Iframes: thinking outside the box
iframe

New post to the Cozi Tech Blog: Iframes: thinking outside the box.

Using an iframe to host some content turned out to be a big pain, so I came up with a different approach.

Contrasting Colors for Text and Background
ColorSchemeDesigner.com

About three weeks ago, I answered a question on StackOverflow about generating the most readable color of text on a colored background.

I suggested flipping the top bit of each component, (r ^ 0x80, g ^ 0x80, b ^ 0x80). This has the same effect as adding 128 if the component is less than 128, and subtracting 128 otherwise.

Another way to think about it is to imagine the 256x256x256 color cube. Inside that cube, erect another cube half as wide. One corner is at your original color and the diagonally opposite corner is the one computed above.

The questioner liked my answer the best, but I decided to experiment further. I wrote some JavaScript to compute that color. As you can see in the table of 549 colors below, it works well most of the time, but it's not perfect.

Someone else suggested an earlier SO question on good-looking font colors. Looking at that thread, I decided to try inverting the HSL value to (h + 180, s, l + (l < 0.5 ? 0.5 : -0.5)). That works well too. Generally, it yields different colors than my first approach. It looks like one of the two always contrasts well.

I found that the most effective approach was to compute the gray-level intensity of the original color, (0.30*r + 0.59*g + 0.11*b). If it was dark, use white; otherwise, black.

Really, though, unless you have a requirement to work with arbitrary colors, you should pick your color scheme carefully. I found a really nice site this afternoon, ColorSchemeDesigner.com

Here's the source of my colortable.

More Posts