summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2024-02-20 11:30:52 +0800
committerSean Whitton <spwhitton@spwhitton.name>2024-02-21 21:21:34 +0800
commita3114123727162db8ffa2be533f938cd6ba658bb (patch)
treebcf574b63c50db4e7d29e50435017cae577ee627
parent221cad0f965712de7104de21530e91e307a172cd (diff)
downloaddotfiles-a3114123727162db8ffa2be533f938cd6ba658bb.tar.gz
i3status-wrapper: add absorb/expel command
-rw-r--r--.config/sway/config13
-rwxr-xr-xscripts/desktop/i3status-wrapper201
2 files changed, 175 insertions, 39 deletions
diff --git a/.config/sway/config b/.config/sway/config
index f9b4c5d6..857f14da 100644
--- a/.config/sway/config
+++ b/.config/sway/config
@@ -91,12 +91,6 @@ mode "C-i-" {
# change focus between tiling / floating windows
bindsym Tab focus mode_toggle, mode "default"
- # focus the parent container
- bindsym u focus parent, mode "default"
-
- # focus the child container
- bindsym d focus child, mode "default"
-
# # when screen is divided into two containers where at least one might
# # have several tabs, as I usually have it, this works well to go back
# # and forth
@@ -172,6 +166,13 @@ mode "C-i-" {
~/src/dotfiles/scripts/desktop/i3status-wrapper-msg \
fresh-workspace take, mode "default"
+ bindsym comma exec \
+ ~/src/dotfiles/scripts/desktop/i3status-wrapper-msg \
+ absorb_expel left, mode "default"
+ bindsym period exec \
+ ~/src/dotfiles/scripts/desktop/i3status-wrapper-msg \
+ absorb_expel right, mode "default"
+
# reload the configuration file
bindsym Ctrl+Mod1+c reload, mode "default"
# exit i3 (logs you out of your X session)
diff --git a/scripts/desktop/i3status-wrapper b/scripts/desktop/i3status-wrapper
index dbc02bad..4e0ddca9 100755
--- a/scripts/desktop/i3status-wrapper
+++ b/scripts/desktop/i3status-wrapper
@@ -30,7 +30,7 @@ use Sys::Hostname;
use POSIX "floor", "mkfifo";
use File::Basename "basename", "dirname";
use File::Spec::Functions "catfile";
-use List::Util qw(first min max);
+use List::Util qw(first min max zip);
$| = 1;
@@ -86,7 +86,8 @@ unless (fork // warn "couldn't fork monitoring loop") {
if (@old_ids) {
fresh_workspace(go => 1);
$wmipc->cmd(
- map("[con_id=$_] move container workspace current", @old_ids),
+ map("[con_id=$_] move container workspace current, floating disable",
+ @old_ids),
"focus child");
}
for_each_node {
@@ -221,52 +222,84 @@ unless (fork // warn "couldn't fork command pipe reader") {
my $ws = $info{paper_ws}{$info{focused_ws}};
my $cols = $ws->{cols};
+ my $col_rows = $info{col_rows}{$ws->{focused_col}};
my $i = first { $cols->[$_] == $ws->{focused_col} } 0..$#$cols;
state $last_dir = 1;
my $mv = sub {
my ($j, $move) = @_;
if (@$cols > $j >= 0) {
- $wmipc->cmd(sprintf "%s %s",
- $move ? "move" : "focus",
- $j > $i ? "right" : "left");
+ if ($move) {
+ # This does not trigger any events.
+ $wmipc->cmd(
+"[con_id=@$cols[$i]] swap container with con_id @$cols[$j]"
+ );
+ @$cols[$i, $j] = @$cols[$j, $i];
+ } else {
+ $wmipc->cmd($j > $i ? "focus right" : "focus left");
+ }
+ } elsif ($move && $ws->{monocle}) {
+ if ($j > $i && $ws->{off_right}->@*) {
+ push $ws->{off_left}->@*, pop $ws->{off_right}->@*;
+ } elsif ($j < $i && $ws->{off_left}->@*) {
+ push $ws->{off_right}->@*, pop $ws->{off_left}->@*;
+ }
} elsif ($j == @$cols && $ws->{off_right}->@*) {
- with_ignored_events {
- my $pushed = shift @$cols;
- my $pulled = pop $ws->{off_right}->@*;
- my @cmds = show_con($pulled);
- push $ws->{off_left}->@*, $pushed;
- if ($move) {
- push @cmds, "focus left", "move right";
+ my $pushed = shift @$cols;
+ my $pulled = pop $ws->{off_right}->@*;
+ my @cmds = show_con($pulled);
+ push $ws->{off_left}->@*, $pushed;
+
+ if ($move) {
+ if ($col_rows || @$cols) {
+ push @cmds, $col_rows
+ ? "move left"
+ : "swap container with con_id @$cols[-1]";
+ push @cmds, "focus right";
+ }
+ if (@$cols) {
my $tem = pop @$cols;
push @$cols, $pulled, $tem;
} else {
- $ws->{focused_col} = $pulled;
push @$cols, $pulled;
}
+ } else {
+ $ws->{focused_col} = $pulled;
+ push @cmds, "move right" if $col_rows;
+ push @$cols, $pulled;
+ push @cmds, "focus child" if $info{col_rows}{$pulled};
+ }
- $wmipc->cmd(@cmds, hide_con($pushed));
- };
+ with_ignored_events { $wmipc->cmd(@cmds, hide_con($pushed)) };
kill USR1 => $i3status;
} elsif ($j == -1 && $ws->{off_left}->@*) {
- with_ignored_events {
- my $pushed = pop @$cols;
- my $pulled = pop $ws->{off_left}->@*;
- my @cmds = show_con($pulled);
- push $ws->{off_right}->@*, $pushed;
+ my $pushed = pop @$cols;
+ my $pulled = pop $ws->{off_left}->@*;
+ my @cmds = show_con($pulled);
- if ($move) {
+ push $ws->{off_right}->@*, $pushed;
+
+ if ($move) {
+ if (@$cols) {
+ push @cmds, "move right" if $col_rows;
push @cmds, "focus left";
my $tem = shift @$cols;
unshift @$cols, $tem, $pulled;
} else {
- push @cmds, "move left";
- $ws->{focused_col} = $pulled;
unshift @$cols, $pulled;
}
+ } else {
+ if ($col_rows) {
+ push @cmds, "move left";
+ } elsif (@$cols) {
+ push @cmds, "swap container with con_id @$cols[0]";
+ }
+ $ws->{focused_col} = $pulled;
+ unshift @$cols, $pulled;
+ push @cmds, "focus child" if $info{col_rows}{$pulled};
+ }
- $wmipc->cmd(@cmds, hide_con($pushed));
- };
+ with_ignored_events { $wmipc->cmd(@cmds, hide_con($pushed)) };
kill USR1 => $i3status;
}
$last_dir = $j > $i ? 1 : -1;
@@ -313,6 +346,55 @@ unless (fork // warn "couldn't fork command pipe reader") {
});
}
+ elsif ($cmd =~ /^absorb_expel ?(left|right)?$/) {
+ my $dir = $1 eq "right" ? 1 : -1;
+ if ($col_rows > 1) { # expel
+ # If the column to the right or left also has rows, we'll just
+ # move the container into that column instead of expelling it.
+ # Possibly we could float the container, select the
+ # appropriate full column and unfloat it into place?
+ $wmipc->cmd(sprintf "move %s", $dir > 0 ? "right" : "left");
+ $last_dir = $dir;
+ } else { # absorb
+ my @cmds;
+ if ($i == 0 && $dir < 0 && $ws->{off_left}->@*) {
+ my $pulled = pop $ws->{off_left}->@*;
+ push @cmds, show_con($pulled), "move left";
+ push @cmds, "splitv" unless $info{col_rows}{$pulled};
+ push @cmds, "focus right", "move left";
+
+ with_ignored_events { $wmipc->cmd(@cmds) };
+ normalise_ws_cols();
+ } elsif ($i == $#$cols && $dir > 0 && $ws->{off_right}->@*) {
+ my $pulled = pop $ws->{off_right}->@*;
+ push @cmds, show_con($pulled);
+ push @cmds, "move right" if $col_rows;
+ push @cmds, "splitv" unless $info{col_rows}{$pulled};
+ push @cmds, "focus left", "move right";
+
+ with_ignored_events { $wmipc->cmd(@cmds) };
+ normalise_ws_cols();
+ } elsif ($i == $#$cols && $dir < 0
+ || $#$cols > $i > 0
+ || $i == 0 && $dir > 0) {
+ push @cmds, $dir > 0
+ ? ("focus right", "splitv", "focus left")
+ : ("focus left", "splitv", "focus right")
+ unless $info{col_rows}{ @$cols[$i+$dir] };
+ push @cmds, $dir > 0 ? "move right" : "move left";
+
+ with_ignored_events { $wmipc->cmd(@cmds) };
+ normalise_ws_cols($ws->{off_left}->@* && $dir > 0
+ || $ws->{off_right}->@* && $dir < 0
+ ? min($#$cols, max 0, $i+$dir) : $i);
+ }
+ if (@cmds) {
+ $last_dir = $dir;
+ kill USR1 => $i3status;
+ }
+ }
+ }
+
tied(%info)->unlock;
}
}
@@ -419,8 +501,9 @@ sub sync_cols {
}
$entry->{cols} = [];
foreach my $child_node ($node->{nodes}->@*) {
- push $entry->{cols}->@*, $child_node->{id}
- if $child_node->{type} eq "con";
+ next unless $child_node->{type} eq "con";
+ push $entry->{cols}->@*, $child_node->{id};
+ $info{col_rows}{$child_node->{id}} = $child_node->{nodes}->@*;
}
}
@@ -452,6 +535,22 @@ sub normalise_ws_cols {
$i = $old_i = !!$avail_l;
}
+ if ($ws->{focused_col} && $info{col_rows}{$ws->{focused_col}}
+ && $info{col_rows}{$ws->{focused_col}} == 1) {
+ # Attempt to delete the vertically split container by moving the
+ # single window it contains over one of its edges.
+ # We can't always do this. We assume the default focus_wrapping.
+ if ($i < $#$cols && !$info{col_rows}{ @$cols[$i+1] }) {
+ push @cmds, "move right";
+ delete $info{col_rows}{$ws->{focused_col}};
+ $ws->{focused_col} = $cols->[$i] = node_first_child($cols->[$i]);
+ } elsif ($i > 0 && !$info{col_rows}{ @$cols[$i-1] }) {
+ push @cmds, "move left";
+ delete $info{col_rows}{$ws->{focused_col}};
+ $ws->{focused_col} = $cols->[$i] = node_first_child($cols->[$i]);
+ }
+ }
+
if (!$ws->{monocle} && $ws->{ncols} > @$cols && ($avail_l || $avail_r)) {
# Pull columns in if there are too few columns but some available.
# Want the focused column, after pulls, to be the $old_i'th.
@@ -491,16 +590,31 @@ sub normalise_ws_cols {
if ($from_l //= min $avail_l, $want-$from_r) {
my @pulled = splice $ws->{off_left}->@*, -$from_l, $from_l;
- push @cmds, ("focus left")x$i,
- map +(show_con($_), "move left"), reverse @pulled;
+ my @to_pull = reverse @pulled;
+ @to_pull = zip \@to_pull, [@$cols[0], @to_pull[0..$#to_pull-1]];
+
+ push @cmds, ("focus left")x$i;
+ for (@to_pull) {
+ push @cmds, show_con(@$_[0]);
+ next unless @$_[1];
+ push @cmds, $info{col_rows}{@$_[1]}
+ ? "move left"
+ : "swap container with con_id @$_[1]";
+ }
+
unshift @$cols, @pulled;
$i = 0;
}
if ($from_r //= min $avail_r, $want-$from_l) {
my @pulled
= reverse splice $ws->{off_right}->@*, -$from_r, $from_r;
- push @cmds, ("focus right")x($#$cols-$i),
- map show_con($_), @pulled;
+ my @to_pull = zip \@pulled, [@$cols[-1], @pulled[1..$#pulled]];
+ push @cmds, ("focus right")x($#$cols-$i);
+ for (@to_pull) {
+ push @cmds, show_con(@$_[0]);
+ push @cmds, "move right" if @$_[1] && $info{col_rows}{@$_[1]};
+ }
+
push @$cols, @pulled;
$i = $#$cols;
}
@@ -512,6 +626,7 @@ sub normalise_ws_cols {
}
$ws->{focused_col} = $cols->[$old_i];
+ push @cmds, "focus child" if $info{col_rows}{$ws->{focused_col}};
}
# Push columns off if there are too many columns.
# This should never change which container is focused.
@@ -628,6 +743,20 @@ sub compact_workspaces {
$opts{leave_gap} and $gap_workspace
}
+sub node_first_child {
+ my $node_id = shift;
+ my $child_id;
+ for_each_node {
+ my $node = shift;
+ if ($node->{id} == $node_id) {
+ $child_id = $node->{nodes}[0]{id};
+ goto DONE;
+ }
+ };
+ DONE:
+ return $child_id;
+}
+
sub sorted_paper_ws {
sort { ws_num($info{paper_ws}{$a}{name})
<=> ws_num($info{paper_ws}{$b}{name}) }
@@ -635,11 +764,17 @@ sub sorted_paper_ws {
}
sub hide_con {
- sprintf "[con_id=%s] move container to workspace %s", $_[0], "*$_[0]*"
+ # Enable floating in order to preserve any rows the container might have.
+ # Otherwise, Sway subsumes the rows to the hidden workspace and the
+ # container with our known ID ceases to exist, s.t. we can't unhide it.
+ sprintf "[con_id=%s] floating enable, move container to workspace %s",
+ $_[0], "*$_[0]*"
}
sub show_con {
- sprintf "[con_id=%s] move container to workspace current, focus", $_[0]
+ sprintf "[con_id=%s] %s",
+ $_[0], join ", ", "move container to workspace current",
+ "floating disable", "focus";
}
sub ws_name {