I've never been one to believe people when they say Perl isn't the right language for something... so I was a little dismayed to find that the sawfish window manager requires extentions commication in LISP. Granted, LISP might be a friendlier language than C (unless you're writing a kernel...), but it doesn't seem to have the same easy support from Perl. Luckily, the Wizards of Sawfish were wise enough to include an interpretter, called "sawfish-client."

What this does is to interface with sawfish-client, and provide access from Perl. I should point out... I don't know LISP. I don't know how to handle LISP objects. You can see evidense in the way I've kludged the managed-windows function. Also you can see it in that my generic function handler, usually called from AUTOLOAD, only accepts one (or zero) X resource id, and squishes in a get-window-by-id. But, it works great for the common window calls...

What is needed is support for other calls, which is mainly dependent on making a list of which is which, and taking making a second generic method without the id kludge.

I'm not going to finish this, because after reading the X library docs, I've realized X11::Protocol is better suited to my needs. So if you think that controlling your wm from Perl is cool, and want to take over this module, please do.

also note, I assume decimal X resource ids, because that is what X11::Protocol provides, but if you use the "eval" method, you'll need to convert them to decorated hex, as that is what sawfish-client expects.

require 5.006; use strict; use warnings; use IPC::Open3; our $VERSION = v0.0.1; our $DEBUG = 99; our $AUTOLOAD; our $SAWFISH_CLIENT = '/usr/bin/sawfish-client'; our @MODULE = qw/ maximize /; sub new { my $proto = shift; my $class = ref $proto || $proto; my $self = {}; bless( $self, $class ); $self->connect or die "failed connecting to sawfish-client '$SAWFI +SH_CLIENT'"; $self->getline; # toss out banner (ahh, the joys of programming by + co-inky-dink...) return $self; } sub AUTOLOAD { my $self = shift; ref $self or return undef; dp( "Autoloaded $AUTOLOAD \@_=@_" ); my $lispfunc = $AUTOLOAD; $lispfunc =~ s/.*://; $lispfunc =~ s/_/-/g; if ( wantarray ) { return( $self->generic_window_command( $lispfunc, @_ ) ); } return scalar $self->generic_window_command( $lispfunc, @_ ); } sub generic_window_command { my $self = shift; my $command = shift; my $id = shift; $id = sprintf( "%#x", 0+$id) if defined $id; # convert to decorated +hex my $lisp = "$command"; $lisp .= " (get-window-by-id $id)" if defined $id; $lisp .= " @_" if @_; my $res = $self->eval( "($lisp)" ); return undef if $res =~ /^\007\*{3}/; if ( wantarray ) { $res =~ s/^\((.*)\)$/$1/; my (@res) = split( ' . ', $res ); return @res; } return $res; } sub fullscreen { my $self = shift; my $win = shift; $self->make_window_ignored( $win ); $self->move_resize_window_to( $win, 0, 0, $self->screen_width, $se +lf->screen_height ); } sub managed_windows { my $self = shift; my $raw = $self->eval( '(managed-windows)' ); my ( @windows ) = map { hex } ($raw =~ /\#<window\s(\w+?)>/g); return @windows; } sub eval { my $self = shift; my @responses = (); foreach my $lisp ( @_ ) { dp( "sending '$lisp'" ); $self->send( $lisp ); my $res = $self->getline; push @responses, $res; dp( "got reply '$res'" ); } return @responses if wantarray; return pop(@responses); } sub send { my $self = shift; my $lisp = join( '', @_ ); $lisp =~ s/[\n\r]/ /g; my $fh = $self->{wfh}; print $fh $lisp,"\n"; } sub getline { my $self = shift; my $fh = $self->{rfh}; <$fh>; # toss echoed input (can this fail? what if input contains +a newline? local $/ = "\nuser> "; # local $/ = "user> "; chomp( my $res = <$fh> ); $res =~ tr/\r//d; return $res; } sub connect { my $self = shift; my @args = ('-'); # meow foreach my $module ( @MODULE ) { unshift( @args, '-r', $module); } dp( "@args" ); $self->{pid} = open3( $self->{wfh}, $self->{rfh}, $self->{efh}, $S +AWFISH_CLIENT, @args ); return $self->{pid}; } sub disconnect { my $self = shift; kill 9, $self->{pid}; waitpid( $self->{pid}, 0 ) if exists $self->{pid}; } sub reconnect { my $self = shift; $self->disconnect; $self->connect; } sub DESTROY { my $self = shift; $self->disconnect; } ### sub dp { return undef unless $DEBUG; my $mess = join( '', @_ ); #$mess =~ s/\n/\\n/g; $mess =~ s/\r/\\r/g; print "DEBUG: >>> $mess <<<\n"; } 1; __END__ =head1 NAME Sawfish - Perl interface to sawfish-client =head1 SYNOPSIS use Sawfish; my $sawfish = new Sawfish(); my $win = $sawfish->get_window_by_name( 'pine' ); die "can't find a window named 'pine'" unless defined $win and $win; $sawfish->x_raise_window( $win ); for (1..5) { $sawfish->make_window_ignored( $win ); sleep 1; $sawfish->make_window_not_ignored( $win ); sleep 1; } =head1 DESCRIPTION Stub documentation for Sawfish. Oops, did I really forget to do this?! =head2 EXPORT None. =head1 AUTHOR Paris Sinclair E<lt>pariss@efn.orgE<gt> =head1 SEE ALSO L<perl>. =cut