diff options
Diffstat (limited to 'bin/laptop/i3-switch-workspace')
-rwxr-xr-x | bin/laptop/i3-switch-workspace | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/bin/laptop/i3-switch-workspace b/bin/laptop/i3-switch-workspace new file mode 100755 index 0000000..8e77cf2 --- /dev/null +++ b/bin/laptop/i3-switch-workspace @@ -0,0 +1,122 @@ +#!/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 $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; +} |