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 --- .config/sway/config | 12 ++-- perl5/Local/Desktop.pm | 83 ---------------------------- scripts/desktop/i3status-wrapper | 115 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 119 insertions(+), 91 deletions(-) diff --git a/.config/sway/config b/.config/sway/config index 492be008..f9b4c5d6 100644 --- a/.config/sway/config +++ b/.config/sway/config @@ -161,16 +161,16 @@ mode "C-i-" { # next unused workspace bindsym j exec \ - ~/src/dotfiles/scripts/desktop/fresh-workspace, \ - mode "default" + ~/src/dotfiles/scripts/desktop/i3status-wrapper-msg \ + fresh-workspace, mode "default" # sends window to next unused but doesn't change view bindsym Mod1+j exec \ - ~/src/dotfiles/scripts/desktop/fresh-workspace --send, \ - mode "default" + ~/src/dotfiles/scripts/desktop/i3status-wrapper-msg \ + fresh-workspace send, mode "default" # sends window to next unused and changes view bindsym Mod1+Ctrl+j exec \ - ~/src/dotfiles/scripts/desktop/fresh-workspace --take, \ - mode "default" + ~/src/dotfiles/scripts/desktop/i3status-wrapper-msg \ + fresh-workspace take, mode "default" # reload the configuration file bindsym Ctrl+Mod1+c reload, mode "default" diff --git a/perl5/Local/Desktop.pm b/perl5/Local/Desktop.pm index f769d827..0460447e 100644 --- a/perl5/Local/Desktop.pm +++ b/perl5/Local/Desktop.pm @@ -33,9 +33,6 @@ use List::Util "first", "any"; use Local::Desktop::WMIPC; our @EXPORT = qw( - @all_workspaces - fresh_workspace - compact_workspaces select_wallpaper_files ensure_resize_for_current_outputs resize_for_current_outputs @@ -43,86 +40,6 @@ our @EXPORT = qw( my $output_re = qr/ ([0-9]+)x([0-9]+)\+([0-9]+)\+([0-9]+) /; -our @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" -); - -=head fresh_workspace($send) - -Switch to the next free workspace, if any. Return the name of that workspace, -or undef if no workspace was available. If $send, send the current window to -the fresh workspace instead of switching focus there. - -=cut - -sub fresh_workspace { - my $next_free_workspace = compact_workspaces(leave_gap => 1); - - if ($next_free_workspace) { - my @cmds; - my %opts = @_; - - push @cmds, "move container to workspace $next_free_workspace" - if $opts{send}; - - # When !$opts{go} we should execute neither of these commands, but we - # must work around . - # - # In the case that !$opts{go}, can use 'C-i S-;' to move any other - # wanted containers over, before finally going there with 'C-i ;'. - push @cmds, "workspace $next_free_workspace"; - push @cmds, "workspace back_and_forth" unless $opts{go}; - - Local::Desktop::WMIPC->new->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 $wmipc = Local::Desktop::WMIPC->new; - my @workspaces; - foreach my $ws ($wmipc->get_workspaces->@*) { - any { $_ eq $ws->{name} } @all_workspaces and push @workspaces, $ws - } - @workspaces < @all_workspaces or return; - my ($current_workspace, $gap_workspace); - if ($opts{leave_gap}) { - $_->{focused} and $current_workspace = $_->{name}, last - for @workspaces - } - - my ($i, @cmds); - while (my $next = shift @workspaces) { - my $workspace = $all_workspaces[$i++]; - $opts{leave_gap} - and $next->{name} eq $current_workspace - and $gap_workspace = $all_workspaces[$i++]; - next if $next->{name} eq $workspace; - my $pair = [$next->{name}, $workspace]; - _wsnum($next->{name}) > _wsnum($workspace) - ? push @cmds, $pair - : unshift @cmds, $pair - } - - $wmipc->cmd(map "rename workspace $_->[0] to $_->[1]", @cmds); - - $opts{leave_gap} and $gap_workspace -} - =head select_wallpaper_files(@files) Select the first entry of @files as the wallpaper for the first output, the 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