From 3f11c295417dd2e7054dec780824f2b955477912 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 19 Jan 2024 12:35:24 +0000 Subject: i3status-wrapper: fix C-i C-M-j --- scripts/desktop/i3status-wrapper | 115 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) (limited to 'scripts') 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: + # . + # (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}) } -- cgit v1.2.3