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
|
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
use IO::Socket::UNIX;
use JSON::PP;
my $json = JSON::PP->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 $prev_workspace");
}
}
}
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 {
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;
}
|