summaryrefslogtreecommitdiff
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
parent2dc6697a4182232c56ec9f1587ad0b713ed9dc3c (diff)
downloaddotfiles-3f11c295417dd2e7054dec780824f2b955477912.tar.gz
i3status-wrapper: fix C-i C-M-j
-rw-r--r--.config/sway/config12
-rw-r--r--perl5/Local/Desktop.pm83
-rwxr-xr-xscripts/desktop/i3status-wrapper115
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 <https://github.com/swaywm/sway/issues/6081>.
- #
- # 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:
+ # <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}) }