summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2024-01-19 12:35:24 +0000
committerSean Whitton <spwhitton@spwhitton.name>2024-01-19 12:39:07 +0000
commit3f11c295417dd2e7054dec780824f2b955477912 (patch)
treeacdf8a946e45f8c59dc187ac199cee4f107c8274 /scripts
parent2dc6697a4182232c56ec9f1587ad0b713ed9dc3c (diff)
downloaddotfiles-3f11c295417dd2e7054dec780824f2b955477912.tar.gz
i3status-wrapper: fix C-i C-M-j
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/desktop/i3status-wrapper115
1 files changed, 113 insertions, 2 deletions
diff --git a/scripts/desktop/i3status-wrapper b/scripts/desktop/i3status-wrapper
index ad5efba1..a15ba291 100755
--- a/scripts/desktop/i3status-wrapper
+++ b/scripts/desktop/i3status-wrapper
@@ -23,7 +23,6 @@ use warnings;
use lib "$ENV{HOME}/src/dotfiles/perl5";
use JSON;
-use Local::Desktop;
use IO::Pipe;
use IPC::Shareable ":lock";
use Local::Desktop::WMIPC;
@@ -54,6 +53,13 @@ sub with_ignored_events (&) {
$wmipc->send_tick("i3status-wrapper-unign");
}
+my @all_workspaces = (
+ "1", "2", "3", "4", "5", "6",
+ "7", "8", "9", "10", "11:F1", "12:F2",
+ "13:F3", "14:F4", "15:F5", "16:F6", "17:F7", "18:F8",
+ "19:F9", "20:F10", "21:F11", "22:F12"
+);
+
unless (fork // warn "couldn't fork monitoring loop") {
my $events = Local::Desktop::WMIPC->new;
$events->subscribe(qw(tick window workspace));
@@ -259,6 +265,7 @@ unless (fork // warn "couldn't fork command pipe reader") {
};
# Command dispatch
+
if ($cmd =~ /^(focus|move) (left|right)$/) {
$mv->($2 eq "right" ? $i+1 : $i-1, $1 eq "move");
} elsif ($cmd =~ /^cols (incr|decr)$/) {
@@ -266,7 +273,9 @@ unless (fork // warn "couldn't fork command pipe reader") {
+= $1 eq "incr" ? 1 : -1;
normalise_ws_cols();
kill USR1 => $i3status;
- } elsif ($cmd =~ /^other column$/) {
+ }
+
+ elsif ($cmd =~ /^other column$/) {
# This is meant to be similar to my custom Emacs C-x o.
if ($i == 0 || $last_dir == -1 && $i < $#$cols) {
$mv->($i+1);
@@ -284,6 +293,18 @@ unless (fork // warn "couldn't fork command pipe reader") {
kill USR1 => $i3status;
}
+ elsif ($cmd =~ /^fresh-workspace ?(take|send)?$/) {
+ fresh_workspace(do {
+ if ($1 && $1 eq "take") {
+ go => 1, send => 1;
+ } elsif ($1 && $1 eq "send") {
+ send => 1;
+ } else {
+ go => 1;
+ }
+ });
+ }
+
tied(%info)->unlock;
}
}
@@ -511,6 +532,96 @@ sub normalise_ws_cols {
}
}
+=head fresh_workspace(%opts)
+
+Switch to the next free workspace, if any. Return the name of that workspace,
+or undef if no workspace was available.
+
+=cut
+
+sub fresh_workspace {
+ my $next_free_workspace = compact_workspaces(leave_gap => 1);
+
+ if ($next_free_workspace) {
+ my @cmds;
+ my %opts = @_;
+
+ # Special case: if we're about to leave a workspace empty by removing
+ # its monocle mode container, then that workspace will get an empty
+ # event, and we'll lose track of any windows pushed off to the sides.
+ # So turn off monocle mode first.
+ my $ws = $info{paper_ws}{$info{focused_ws}};
+ if (my $m = $ws->{monocle}) {
+ undef $ws->{monocle};
+ normalise_ws_cols(abs ++$m);
+ }
+
+ # We need to ensure that the monitoring loop doesn't process the move
+ # event before it knows about the workspace change. Otherwise, that
+ # loop might try to unhide containers from the old workspace onto the
+ # new one. We do need it to process the workspace init event, else we
+ # don't know the ID of the new workspace without making our own query.
+ #
+ # We also want to ensure that the fresh workspace is the one that
+ # C-i ; will take us to. In the case that !$opts{go}, can use C-i M-j
+ # to move any other wanted containers over, before a final C-i ;.
+ #
+ # There is a relevant i3/Sway difference here:
+ # <https://github.com/swaywm/sway/issues/6081>.
+ # (Our use of hide_con elsewhere assumes Sway's behaviour. Possibly
+ # we should write wrapper code that can handle either case.)
+
+ push @cmds, "workspace $next_free_workspace";
+ push @cmds, show_con($info{paper_ws}{$info{focused_ws}}{focused_col})
+ if $opts{send};
+ push @cmds, "workspace back_and_forth" unless $opts{go};
+
+ $wmipc->cmd(@cmds);
+ }
+ $next_free_workspace
+}
+
+=head compact_workspaces(%opts)
+
+Rename workspaces so as to remove gaps in the sequence of workspaces.
+
+If C<$opts{leave_gap}>, ensure there is a gap of one workspace after the
+currently focused workspace and return the name of the gap workspace, or just
+return undef if there is no space for a gap.
+
+=cut
+
+sub compact_workspaces {
+ my %opts = @_;
+ my @workspaces = sorted_paper_ws();
+ @workspaces < @all_workspaces or return;
+ my ($i, $gap_workspace, @pairs);
+
+ while (my $next = shift @workspaces) {
+ my $workspace = $all_workspaces[$i++];
+
+ $opts{leave_gap}
+ and $next == $info{focused_ws}
+ and $gap_workspace = $all_workspaces[$i++];
+ my $next_name = $info{paper_ws}{$next}{name};
+ next if $next_name eq $workspace;
+ my $pair = [$next, $workspace];
+ ws_num($next_name) > ws_num($workspace)
+ ? push @pairs, $pair
+ : unshift @pairs, $pair
+ }
+
+ with_ignored_events {
+ $wmipc->cmd(
+ map sprintf("rename workspace %s to %s",
+ $info{paper_ws}{$_->[0]}{name}, $_->[1]),
+ @pairs)
+ };
+ $info{paper_ws}{$_->[0]}{name} = $_->[1] for @pairs;
+
+ $opts{leave_gap} and $gap_workspace
+}
+
sub sorted_paper_ws {
sort { ws_num($info{paper_ws}{$a}{name})
<=> ws_num($info{paper_ws}{$b}{name}) }