summaryrefslogtreecommitdiffstats
path: root/bin/i3-switch-workspace
blob: a90be29dc8f779ccfc32a87f88ec052850dc9ee8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env perl
use strict;
use warnings;

use IO::Socket::UNIX;
use JSON;

my $json = JSON->new;

my %message_types = (
    command        => 0,
    get_workspaces => 1,
    subscribe      => 2,
    get_outputs    => 3,
    get_tree       => 4,
    get_marks      => 5,
    get_bar_config => 6,
    get_version    => 7,
);

my %message_codes = (reverse %message_types);

my $dir = $ARGV[0];

chomp(my $path = qx(i3 --get-socketpath));
my $sock = IO::Socket::UNIX->new(Peer => $path);

my $workspace_data = $json->decode(i3_msg('get_workspaces'));

my $next_workspace = $workspace_data->[-1]{num} + 1;
my $prev_workspace = $workspace_data->[0]{num} - 1;

if (@$workspace_data == 1) {
    my $current_workspace = $workspace_data->[0]{num};
    my $tree_data = $json->decode(i3_msg('get_tree'));
    my $workspace_tree = find_workspace($tree_data, $current_workspace);
    if ($dir eq 'prev') {
        i3_msg('command', "workspace number $prev_workspace");
    }
    else {
        i3_msg('command', "workspace number $next_workspace");
    }
}
elsif ($workspace_data->[0]{focused} && $dir eq 'prev') {
    my $current_workspace = $workspace_data->[0]{num};
    my $tree_data = $json->decode(i3_msg('get_tree'));
    my $workspace_tree = find_workspace($tree_data, $current_workspace);
    if (@{ $workspace_tree->{nodes} } || @{ $workspace_tree->{floating_nodes} }) {
        if ($prev_workspace < 0) {
            i3_msg('command', "workspace number $next_workspace");
        }
        else {
            i3_msg('command', "workspace number $prev_workspace");
        }
    }
    else {
        i3_msg('command', "workspace $dir");
    }
}
elsif ($workspace_data->[-1]{focused} && $dir eq 'next') {
    my $current_workspace = $workspace_data->[-1]{num};
    my $tree_data = $json->decode(i3_msg('get_tree'));
    my $workspace_tree = find_workspace($tree_data, $current_workspace);
    if (@{ $workspace_tree->{nodes} } || @{ $workspace_tree->{floating_nodes} }) {
        i3_msg('command', "workspace number $next_workspace");
    }
    else {
        i3_msg('command', "workspace $dir");
    }
}
else {
    for my $i (0..$#$workspace_data) {
        if ($workspace_data->[$i]{focused}) {
            my $to = $workspace_data->[$i]{num} + ($dir eq 'prev' ? -1 : 1);
            i3_msg('command', "workspace number $to");
            last;
        }
    }
}

sub find_workspace {
    my ($tree, $num) = @_;
    if (exists $tree->{num} && $tree->{num} == $num) {
        return $tree;
    }
    for my $node (@{ $tree->{nodes} }) {
        my $found = find_workspace($node, $num);
        return $found if $found;
    }
    return;
}

sub build_i3_msg {
    my ($type, $payload) = @_;

    $payload = ''
        unless $type eq 'command' || $type eq 'subscribe';

    utf8::encode($payload);

    return 'i3-ipc'
         . pack('LL', length($payload), $message_types{$type})
         . $payload;
}

sub get_i3_msg {
    my $bytes = $sock->read(my $header, 14);
    die "invalid read" if $bytes != 14;
    die "invalid format: $header" if substr($header, 0, 6) ne 'i3-ipc';
    my ($length, $type) = unpack('LL', substr($header, 6, 8));

    if ($length) {
        my $bytes = $sock->read(my $payload, $length);
        die "invalid read" if $bytes != $length;
        utf8::decode($payload);
        return ($message_codes{$type}, $payload);
    }
    else {
        return ($message_codes{$type});
    }
}

sub i3_msg {
    my ($type, $payload) = @_;
    my $msg = build_i3_msg($type, $payload);
    $sock->write($msg);
    my ($got_type, $got_payload) = get_i3_msg();
    die "got $got_type message, expected $type message"
        unless $type eq $got_type;
    return $got_payload;
}