AnyEvent-I3

 view release on metacpan or  search on metacpan

lib/AnyEvent/I3.pm  view on Meta::CPAN


    $i3->connect->recv or die "Error connecting";
    say "Connected to i3";

    my $workspaces = $i3->message(TYPE_GET_WORKSPACES)->recv;
    say "Currently, you use " . @{$workspaces} . " workspaces";

...or, using the sugar methods:

    use AnyEvent::I3;

    my $workspaces = i3->get_workspaces->recv;
    say "Currently, you use " . @{$workspaces} . " workspaces";

A somewhat more involved example which dumps the i3 layout tree whenever there
is a workspace event:

    use Data::Dumper;
    use AnyEvent;
    use AnyEvent::I3;

    my $i3 = i3();

    $i3->connect->recv or die "Error connecting to i3";

    $i3->subscribe({
        workspace => sub {
            $i3->get_tree->cb(sub {
                my ($tree) = @_;
                say "tree: " . Dumper($tree);
            });
        }
    })->recv->{success} or die "Error subscribing to events";

    AE::cv->recv

=head1 EXPORT

=head2 $i3 = i3([ $path ]);

Creates a new C<AnyEvent::I3> object and returns it.

C<path> is an optional path of the UNIX socket to connect to. It is strongly
advised to NOT specify this unless you're absolutely sure you need it.
C<AnyEvent::I3> will automatically figure it out by querying the running i3
instance on the current DISPLAY which is almost always what you want.

=head1 SUBROUTINES/METHODS

=cut

use Exporter qw(import);
use base 'Exporter';

our @EXPORT = qw(i3);

use constant TYPE_RUN_COMMAND => 0;
use constant TYPE_COMMAND => 0;
use constant TYPE_GET_WORKSPACES => 1;
use constant TYPE_SUBSCRIBE => 2;
use constant TYPE_GET_OUTPUTS => 3;
use constant TYPE_GET_TREE => 4;
use constant TYPE_GET_MARKS => 5;
use constant TYPE_GET_BAR_CONFIG => 6;
use constant TYPE_GET_VERSION => 7;
use constant TYPE_GET_BINDING_MODES => 8;
use constant TYPE_GET_CONFIG => 9;
use constant TYPE_SEND_TICK => 10;
use constant TYPE_SYNC => 11;
use constant TYPE_GET_BINDING_STATE => 12;

our %EXPORT_TAGS = ( 'all' => [
    qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS
       TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION
       TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC
       TYPE_GET_BINDING_STATE)
] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );

my $magic = "i3-ipc";

# TODO: auto-generate this from the header file? (i3/ipc.h)
my $event_mask = (1 << 31);
my %events = (
    workspace => ($event_mask | 0),
    output => ($event_mask | 1),
    mode => ($event_mask | 2),
    window => ($event_mask | 3),
    barconfig_update => ($event_mask | 4),
    binding => ($event_mask | 5),
    shutdown => ($event_mask | 6),
    tick => ($event_mask | 7),
    _error => 0xFFFFFFFF,
);

sub i3 {
    AnyEvent::I3->new(@_)
}

# Calls i3, even when running in taint mode.
sub _call_i3 {
    my ($args) = @_;

    my $path_tainted = tainted($ENV{PATH});
    # This effectively circumvents taint mode checking for $ENV{PATH}. We
    # do this because users might specify PATH explicitly to call i3 in a
    # custom location (think ~/.bin/).
    (local $ENV{PATH}) = ($ENV{PATH} =~ /(.*)/);

    # In taint mode, we also need to remove all relative directories from
    # PATH (like . or ../bin). We only do this in taint mode and warn the
    # user, since this might break a real-world use case for some people.
    if ($path_tainted) {
        my @dirs = split /:/, $ENV{PATH};
        my @filtered = grep !/^\./, @dirs;
        if (scalar @dirs != scalar @filtered) {
            $ENV{PATH} = join ':', @filtered;
            warn qq|Removed relative directories from PATH because you | .
                 qq|are running Perl with taint mode enabled. Remove -T | .
                 qq|to be able to use relative directories in PATH. | .
                 qq|New PATH is "$ENV{PATH}"|;
        }
    }
    # Otherwise the qx() operator wont work:
    delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
    chomp(my $result = qx(i3 $args));
    # Circumventing taint mode again: the socket can be anywhere on the
    # system and that’s okay.
    if ($result =~ /^([^\0]+)$/) {
        return $1;
    }

lib/AnyEvent/I3.pm  view on Meta::CPAN


    my $cv = AnyEvent->condvar;

    # We don’t preserve the old callback as it makes no sense to
    # have a callback on message reply types (only on events)
    $self->{callbacks}->{$type} =
        sub {
            my ($reply) = @_;
            $cv->send($reply);
            undef $self->{callbacks}->{$type};
        };

    $cv
}

=head1 SUGAR METHODS

These methods intend to make your scripts as beautiful as possible. All of
them automatically establish a connection to i3 blockingly (if it does not
already exist).

=cut

sub _ensure_connection {
    my ($self) = @_;

    return if defined($self->{ipchdl});

    $self->connect->recv or confess "Unable to connect to i3 (socket path " . $self->{path} . ")";
}

=head2 get_workspaces

Gets the current workspaces from i3.

    my $ws = i3->get_workspaces->recv;
    say Dumper($ws);

=cut
sub get_workspaces {
    my ($self) = @_;

    $self->_ensure_connection;

    $self->message(TYPE_GET_WORKSPACES)
}

=head2 get_outputs

Gets the current outputs from i3.

    my $outs = i3->get_outputs->recv;
    say Dumper($outs);

=cut
sub get_outputs {
    my ($self) = @_;

    $self->_ensure_connection;

    $self->message(TYPE_GET_OUTPUTS)
}

=head2 get_tree

Gets the layout tree from i3 (>= v4.0).

    my $tree = i3->get_tree->recv;
    say Dumper($tree);

=cut
sub get_tree {
    my ($self) = @_;

    $self->_ensure_connection;

    $self->message(TYPE_GET_TREE)
}

=head2 get_marks

Gets all the window identifier marks from i3 (>= v4.1).

    my $marks = i3->get_marks->recv;
    say Dumper($marks);

=cut
sub get_marks {
    my ($self) = @_;

    $self->_ensure_connection;

    $self->message(TYPE_GET_MARKS)
}

=head2 get_bar_config

Gets the bar configuration for the specific bar id from i3 (>= v4.1).

    my $config = i3->get_bar_config($id)->recv;
    say Dumper($config);

=cut
sub get_bar_config {
    my ($self, $id) = @_;

    $self->_ensure_connection;

    $self->message(TYPE_GET_BAR_CONFIG, $id)
}

=head2 get_version

Gets the i3 version via IPC, with a fall-back that parses the output of i3
--version (for i3 < v4.3).

    my $version = i3->get_version()->recv;
    say "major: " . $version->{major} . ", minor = " . $version->{minor};

=cut
sub get_version {



( run in 1.286 second using v1.01-cache-2.11-cpan-d7f47b0818f )