Wednesday, March 1, 2006

Picking a random color for xterms

More simple utility code. This time, it's a perl script that pulls a random color from the X11 color list (rgb.txt). It's really handy for getting a random foreground (or background) color in an xterm:

xterm -fg `~/bin/randomcolor.pl` -bg black

In Apple's X11, I modified the cmd-N command to do this -- so I can hit cmd-N and get a bunch of colorful terminals. Anyway, here's the code (Updated 2 March to incorporate the suggestions Randal L. Schwartz left in the comments):

#!/usr/bin/perl

use strict;

my @RGBFILE = qw(/usr/lib/X11/rgb.txt /usr/X11R6/lib/X11/rgb.txt);

# open the first file on the list that works...
RGB_FILE: foreach my $filename (@RGBFILE) {
if((-e $filename)&&(-s $filename)&&(-r $filename)&&(-T $filename)) {
# file exists, has nonzero size, is readable, and is a text file
open(RGBVALS,"<$filename");
last RGB_FILE;
} else {
next RGB_FILE;
}
}

my @color_names;
my %colors_seen;

my $limit = 300;

COLOR: while(<RGBVALS>) {

next COLOR unless ($_ =~ /^\s*(\d+\s+){3}[a-zA-Z]+$/);
#remove leading whitespace
$_ =~ s/^\s+//;

my ($r,$g,$b,$name) = split(/\s+/,$_,4);

# next COLOR unless $name =~ /^[a-zA-Z]+$/;

my $lcname = $name;
$lcname =~ tr/A-Z/a-z/;

# skip colors that aren't very readable
next COLOR if $lcname =~ /gr[ae]y/;
next COLOR if $lcname =~ /black/;
next COLOR if $lcname =~ /^dark$/;

# skip gray-ish colors
my $rg = ($r - $g)**2;
my $rb = ($r - $b)**2;
my $gb = ($g - $b)**2;
next COLOR if (($rg < $limit) && ($rb < $limit) && ($gb < $limit));

# skip duplicates
my $key = join(":",$r,$g,$b);
next COLOR if $colors_seen{$key};
$colors_seen{$key}++;

# add the name to the array to select from
push(@color_names,$name);

}

close RGBVALS;

# put something in there if no colors were found
push(@color_names,"white") if ($#color_names < 0);

# pick a random color for output
print "$color_names[rand @color_names]";

Enjoy!

3 comments:

Randal L. Schwartz said...

Argh. Don't do the srand() thing. That hasn't been needed for 10 years, and it'll actually give you a slightly less-secure random number. (What Perl does inside is even more perverse than that!)

Also, you're always throwing away the last item of your resulting array. The idiom you want is:

print $some_array[rand @some_array]

This picks any element of @some_array with equal weight.

a said...

Thanks for the feedback Randal. I've updated the code in the post to reflect these suggestions.

The srand() thing was pulled straight out of the O'Reilly Perl Cookbook at some point and I've just carried it around in code since then.

a said...

I hadn't noticed until I just tried to run the script posted here on a new machine that there was an unescaped angle bracket in the text I had posted. You'll now notice that the while() loop is no longer infinite, and instead just reads through the <RGBVALS> file instead.

www.flickr.com