Just a little fun with the Cairo Perl bindings. The code is courtesy of the gtkmm tutorial, and can be found here.
Update: since the original website went down, here’s the inlined version of the script:
package Egg::ClockFace;
use warnings;
use strict;
use Glib qw/TRUE FALSE/;
use Gtk2;
use Cairo;
use Math::Trig;
use Glib::Object::Subclass
Gtk2::DrawingArea::,
signals => {
expose_event => \&expose,
};
sub min { return ($_[0] < $_[1] ? $_[0] : $_[1]); }
sub draw
{
my $self = shift;
my $cr = $self->{cr};
return FALSE unless $cr;
my $width = $self->allocation->width;
my $height = $self->allocation->height;
$cr->scale($width, $height);
$cr->translate(0.5, 0.5);
$cr->set_line_width($self->{line_width});
$cr->save;
$cr->set_source_rgba (0.337, 0.612, 0.117, 0.9);
$cr->paint;
$cr->restore;
$cr->arc (0, 0, $self->{radius}, 0, 2 * Math::Trig::pi);
$cr->save;
$cr->set_source_rgba (1.0, 1.0, 1.0, 0.8);
$cr->fill_preserve;
$cr->restore;
$cr->stroke_preserve;
$cr->clip;
for (1 .. 12) {
my $inset = 0.05;
$cr->save;
$cr->set_line_cap('round');
if ($_ % 3 != 0) {
$inset *= 0.8;
$cr->set_line_width(0.03);
}
$cr->move_to(($self->{radius} - $inset) * cos ($_ * Math::Trig::pi / 6),
($self->{radius} - $inset) * sin ($_ * Math::Trig::pi / 6));
$cr->line_to($self->{radius} * cos ($_ * Math::Trig::pi / 6),
$self->{radius} * sin ($_ * Math::Trig::pi / 6));
$cr->stroke;
$cr->restore;
}
my @time = localtime;
my $hours = $time[2] * Math::Trig::pi / 6;
my $minutes = $time[1] * Math::Trig::pi / 30;
my $seconds = $time[0] * Math::Trig::pi / 30;
$cr->save;
$cr->set_line_cap('round');
# seconds
$cr->save;
$cr->set_line_width($self->{line_width} / 3);
$cr->set_source_rgba(1.0, 0.0, 0.0, 0.8);
$cr->move_to(0, 0);
$cr->line_to( sin($seconds) * ($self->{radius} * .9),
-1 * cos($seconds) * ($self->{radius} * .9));
$cr->stroke;
$cr->restore;
# minutes;
$cr->set_source_rgba(0.7, 0.7, 0.7, 0.8);
$cr->move_to(0, 0);
$cr->line_to( sin($minutes + $seconds / 60) * ($self->{radius} * 0.8),
-1 * cos($minutes + $seconds / 60) * ($self->{radius} * 0.8));
$cr->stroke;
# hours
$cr->set_source_rgba(0.117, 0.337, 0.612, 0.9);
$cr->move_to(0, 0);
$cr->line_to( sin($hours + $minutes / 12.0) * ($self->{radius} * 0.5),
-1 * cos($hours + $minutes / 12.0) * ($self->{radius} * 0.5));
$cr->stroke;
$cr->restore;
# dot
$cr->arc(0, 0, $self->{line_width} / 3.0, 0, 2 * Math::Trig::pi);
$cr->fill;
return TRUE;
}
sub expose
{
my ($self, $event) = @_;
my $cr = Gtk2::Gdk::Cairo::Context->create($self->window);
$cr->rectangle ($event->area->x,
$event->area->y,
$event->area->width,
$event->area->height);
$cr->clip;
$self->{cr} = $cr;
$self->draw;
$self->{timeout} = Glib::Timeout->add(1000, sub {
my $self = shift;
my $alloc = $self->allocation;
my $rect = Gtk2::Gdk::Rectangle->new(0, 0, $alloc->width, $alloc->height);
$self->window->invalidate_rect($rect, FALSE);
return TRUE;
}, $self) unless $self->{timeout};
return FALSE;
}
sub INIT_INSTANCE
{
my $self = shift;
$self->{line_width} = 0.05;
$self->{radius} = 0.42;
}
sub FINALIZE_INSTANCE
{
my $self = shift;
Glib::Source->remove($self->{timeout}) if $self->{timeout};
}
1;
package main;
use Gtk2 '-init';
my $window = Gtk2::Window->new('toplevel');
my $clock = Egg::ClockFace->new;
$window->add($clock);
$window->signal_connect(destroy => sub { Gtk2->main_quit; });
$window->show_all;
Gtk2->main;
0;