X11::Xvfb

http://i.work.at.wbs.net.au/moztest.txtをコピペしてXvfbのOOPなラッパーを書いた。スクリーンショットサーバをPerlで作りたい人用。moztest.txtのライセンスがよくわからない(as-is)のでアレなんだけど、それを主張してきたとしてもたぶんここまで変わってれば大丈夫だと思う。

usage

use X11::Xvfb;

my $xvfb = X11::Xvfb->new(
    xvfb => "/usr/bin/Xvfb",
    xsockets => "/tmp/.X11-unix",
    width => 1024,
    height => 768,
    depth => 24,
);

$xvfb->open;

(ここでfirefox起動してスクリーンショット撮ったりする)

$xvfb->close;

X11/Xvfb.pm

package X11::Xvfb;
use strict;
use warnings;

use IPC::Open2;

use base qw( Class::Accessor::Fast );
__PACKAGE__->mk_accessors(qw/xvfb xsockets config pid display/);

sub new {
    my $pkg = shift;
    my %opt = @_;
    
    my $xvfb_path = $opt{xvfb} or die;
    my $xsockets_path = $opt{xsockets} or die;
    
    my $config = {
        width => $opt{width} || 1280,
        height => $opt{height} || 960,
        depth => $opt{depth} || 24,
    };
    
    my $hash = {
        xvfb => $xvfb_path,
        xsockets => $xsockets_path,
        config => $config,
        pid => undef,
        display => undef,
    };
    
    bless $hash, $pkg;
}

sub open {
    my $self = shift;
    
    $SIG{CHLD} = 'IGNORE';
    
    my $PathToXSockets = $self->xsockets;
    my @Sockets =
        sort {$a <=> $b}
        map {$_ =~ s/^.+X//; $_} <$PathToXSockets/*>;
    
    my ($last, $current) = (0,0);
    for my $num (@Sockets) {
        if ($last+1 < $num) {
            $current = $last+1;
            last;
        }
        $last = $num;
    }
    
    $current = (($last > 0) ? $last + 1 : 1) if ($current == 0);
    
    $ENV{'DISPLAY'} = ":$current";
    
    my $PathToXvfb = $self->xvfb;
    my $configs = sprintf "%dx%dx%d",
        $self->config->{width},
        $self->config->{height},
        $self->config->{depth};
    
    my $pid = open2(
        \*O,
        \*I,
        "$PathToXvfb :$current -once -screen 0 $configs"
    ) or die($!);
    
    close(*I); close(*O);
    $self->display($current);
    $self->pid($pid);
}

sub close {
    my $self = shift;
    
    kill(2, $self->pid);
    kill(2, $self->pid);
    
    $self->display('');
    $self->pid('');
}

sub DESTROY {
    my $self = shift;
    
    if ($self->pid) {
        $self->close;
    }
}

1;