http://qs321.pair.com?node_id=620221
Category: GUI Programming
Author/Contact Info
Description: Since there's been no activity in the GUI programming category for nearly 4 years, maybe it's appropriate that this code is something I wrote in the early 2000s, teaching myself basic Gtk while commuting to and from work. Inspirations: the Atari 2600 "Surround" cartridge and graphics programming on the Apple II and Atari 800, and randomness. Quick notes: the escape key erases the canvas. Other mouse and key events have little effect (try & see), and see the code for some undocumented command-line options (redundant; the whole thing's undocumented, unless you count this). Mainly it was fun to write and kind of fun to watch run.
#!/usr/local/bin/perl -w

use strict;
use integer;
use Getopt::Std;
use Gtk;
use vars qw($automatic $window $canvas $pixmap $r_gc $width $height $b
+s
            $orig_bs $shrink $VERBOSE $x $y);

my %o;
getopts('ab:svx:y:', \%o);

$VERBOSE = $o{'v'};
$shrink = $o{'s'};
$automatic = $o{'a'};
$width = $o{'x'} || 400;
$height = $o{'y'} || $o{'x'} || 400;
$bs = $o{'b'} || 10;   # each "block" is 10x10 pixels by default
$orig_bs = $bs;
$x = int($width/$bs/2);
$y = int($height/$bs/2);
my $timeout = 512; # milliseconds
my $timer;
my @grid;

my %moves = (
    65361 => sub { $x = int($width/$bs)-1 if --$x < 0 },  # west
    65362 => sub { $y = int($height/$bs)-1 if --$y < 0 }, # north
    65363 => sub { $x = 0 if ++$x >= int($width/$bs) },   # east
    65364 => sub { $y = 0 if ++$y >= int($height/$bs) },  # south
);

{
    my $prev_rand = 65361;
    sub auto_move {
        my $rand = 65361 + int rand 4;
        if ( ($rand != $prev_rand) && (($rand % 2) == ($prev_rand % 2)
+) ) {
            $rand = $prev_rand;
        }
        $prev_rand = $rand;
        &{$moves{$rand}};
        my $many = ($width/$bs) * ($height/$bs);
        while(defined $grid[$x]->[$y]) {
            $x = int(rand($width/$bs));
            $y = int(rand($height/$bs));
            print "attempt: ($x, $y)\n" if $VERBOSE;
            return 0 if --$many < 0;
            Gtk->timeout_remove($timer);
            $timeout = $timeout/2;
            #return 0 if $timeout < 1;
            $timeout = 1 if $timeout < 1;
            $timer = Gtk->timeout_add($timeout, \&auto_move, ());
            if ($shrink) {
                $bs--;
                $bs=$orig_bs if $bs < 1;
            }
        }
        random_color($canvas);
        block_plot($x, $y);
        return 1;
    }
}

init Gtk;

$window = Gtk::Window->new('toplevel');
$window->signal_connect('destroy', sub { $window->destroy; exit 0 });
$window->signal_connect('key_press_event', \&key_handler);
$window->set_title('blox');

my $vbox = Gtk::VBox->new(0, 0);
$window->add($vbox);
$vbox->show;

$canvas = Gtk::DrawingArea->new;
$canvas->size($width, $height);

$canvas->signal_connect('expose_event', \&pixmap_expose);

$vbox->pack_start($canvas, 1, 1, 0);
$canvas->set_events([
    'exposure_mask',
    'button_press_mask',
    'button1-motion-mask',
    'button2-motion-mask',
    'button3-motion-mask',
]);
$canvas->signal_connect('button_press_event', \&mouse_click);
$canvas->signal_connect('motion_notify_event', \&mouse_click);
$canvas->show;
$canvas->realize;
$pixmap = Gtk::Gdk::Pixmap->new($canvas->window, $width, $height, -1);
$r_gc = Gtk::Gdk::GC->new($canvas->window);
*random_color = \&set_random_color;

{
    my %c = (
        'red'   => int(rand 65536),
        'green' => int(rand 65536),
        'blue'  => int(rand 65536)
    );

    sub set_random_progressive_color {
        my $widget = shift || return undef;
        COLOR: for(keys %c) {
            while(1) {
                $c{$_} += (int(rand 3)-1) * int(rand 4096);
                next COLOR if ($c{$_} > 0) && ($c{$_} < 65536);
            }
        }
        my $r_color = $widget->window->get_colormap->color_alloc(\%c);
        $r_gc->set_foreground($r_color);
        1;
    }
}

random_color($canvas);

sub block_plot {
    my $x = shift; my $y = shift;
    $grid[$x] = [] unless defined $grid[$x];
    $grid[$x]->[$y] = 1;
#    $pixmap->draw_rectangle($r_gc, 1, $x*$bs, $y*$bs, $bs, $bs); # fi
+lled
    $pixmap->draw_rectangle($r_gc, 0, $x*$bs, $y*$bs, $bs, $bs); # out
+line
    $canvas->window->draw_pixmap(
        $canvas->style->fg_gc('normal'),
        $pixmap, 0, 0, 0, 0, $width, $height
    );
    1;
}

sub blank_canvas {
    $bs = $orig_bs;
    my $widget = shift;
    my $r_color = $widget->window->get_colormap->color_alloc(
        { 'red' => 0, 'green' => 0, 'blue' => 0 }
    );
    $r_gc->set_foreground($r_color);
    $pixmap->draw_rectangle($r_gc, 1, 0, 0, $width, $height);
    $widget->window->draw_pixmap(
        $widget->style->fg_gc('normal'),
        $pixmap, 0, 0, 0, 0, $width, $height
    );
    @grid = ();
}

$window->show;
$timer = Gtk->timeout_add($timeout, \&auto_move, ());

main Gtk;

{
    my $previous_button = 1;
    sub mouse_click {
        my $widget = shift;
        my $event  = shift;
        $x = int($event->{'x'}/$bs);
        $y = int($event->{'y'}/$bs);
        $previous_button = $event->{'button'} if defined $event->{'but
+ton'};
        if ($previous_button == 1) {
            set_random_color($canvas);
        } elsif ($previous_button == 2) {
            set_random_progressive_color($canvas);
        } else {
            # don't change the color for button 3
        }
        block_plot($x, $y);
        1;
    }
}

sub pixmap_expose {
    my $widget = shift;
    my $event  = shift;
    my ($x1, $y1, $x2, $y2) = @{$event->{'area'}};
    $widget->window->draw_pixmap(
        $widget->style->fg_gc('normal'),
        $pixmap, $x1, $y1, $x1, $y1, $x2, $y2);
    1;
}

sub set_random_color {
    my $widget = shift || return undef;
    my $r_color = $widget->window->get_colormap->color_alloc(
        {
            'red'   => int(rand 65536),
            'green' => int(rand 65536),
            'blue'  => int(rand 65536),
        }
    );
    $r_gc->set_foreground($r_color);
}

sub key_handler {
    my $widget = shift;
    my $event = shift;
    my $l = chr($event->{'keyval'});
    if ($event->{'keyval'} == 65307) {
        blank_canvas($canvas);
        random_color($canvas);
        $timeout = 512;
    } elsif (defined $moves{$event->{'keyval'}}) {
        &{$moves{$event->{'keyval'}}};
        my $many = ($width/$bs) * ($height/$bs);
        while(defined $grid[$x]->[$y]) {
            $x = int(rand($width/$bs));
            $y = int(rand($height/$bs));
            return 1 if --$many < 0;
        }
        random_color($canvas);
        block_plot($x, $y);
    } elsif ($event->{'keyval'} == 32) { # space
#        *random_color
    } else {
        print STDERR "$$event{'keyval'}\n";
    }
    1;
}