summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2024-01-14 19:38:39 +0000
committerSean Whitton <spwhitton@spwhitton.name>2024-01-16 17:17:31 +0000
commit14b917b5779e350a10db2397ff4da788a0945dcc (patch)
treeae32f832d37876a8692e4c440dae865ea2e9c418
parent7dcda7d25202c7a78a6f78cd28b90d03e5bdd4c2 (diff)
downloaddotfiles-14b917b5779e350a10db2397ff4da788a0945dcc.tar.gz
i3status-wrapper: improve & simplify normalising the number of cols
-rwxr-xr-xscripts/desktop/i3status-wrapper206
1 files changed, 101 insertions, 105 deletions
diff --git a/scripts/desktop/i3status-wrapper b/scripts/desktop/i3status-wrapper
index 7b6087b4..7fe552a9 100755
--- a/scripts/desktop/i3status-wrapper
+++ b/scripts/desktop/i3status-wrapper
@@ -71,37 +71,31 @@ unless (fork // warn "couldn't fork monitoring loop") {
$wmipc->cmd(map("[con_id=$_] move container workspace current", @old_ids),
"focus child");
}
- update_paper_ws_cols();
-
- # Now loop forever reading events, assuming no exceptions.
-
- sub new_container {
- # New window on one of our monitored workspaces.
- # Push a window off the workspace to accommodate new one.
- # Leave it to a later loop iteration to update the focus.
- my $ws = $info{paper_ws}{$info{focused_ws}};
- my $cols = $ws->{cols};
- if ($ws->{cols}->@*) {
- my $i = first { $cols->[$_] == $ws->{focused_col} } 0..$#$cols;
- splice $cols->@*, $i+1, 0, shift->{container}{id};
- if ($ws->{monocle} || $cols->@* > $ws->{ncols}) {
- with_ignored_events {
- my $pushed = shift $cols->@*;
- $wmipc->cmd(hide_con($pushed));
- push $ws->{off_left}->@*, $pushed;
- };
+ my @trees = $wmipc->get_tree;
+ while (@trees) {
+ foreach my $node ((shift @trees)->{nodes}->@*) {
+ if ($node->{type} eq "workspace"
+ && grep $_ eq $node->{name}, @all_workspaces) {
+ my $entry = $info{paper_ws}{$node->{id}}
+ //= { off_left => [], off_right => [] };
+ sync_cols($node => $entry);
+ $entry->{ncols} = max 2, scalar $entry->{cols}->@*;
+ } elsif (grep $_ eq "caffeinated", $node->{marks}->@*) {
+ register_caffeinated($node);
}
- } else {
- push $ws->{cols}->@*, shift->{container}{id};
+ unshift @trees, $node;
}
}
+
+ # Now loop forever reading events, assuming no exceptions.
+
eval {
while (my $e = <$events>) {
state $last_e;
# New containers
if ($last_e && $last_e->{change} && $last_e->{change} eq "new") {
- new_container($last_e)
+ normalise_ws_cols()
unless $e->{change} && $e->{change} eq "floating";
undef $last_e;
} elsif ($e->{change} && $e->{change} eq "new"
@@ -113,67 +107,24 @@ unless (fork // warn "couldn't fork monitoring loop") {
&& $e->{change} eq "floating"
&& $e->{container}{type} ne "floating_con") {
# A container stopped floating -- it's as though it's new.
- new_container($e);
- $info{paper_ws}{$info{focused_ws}}{focused_col}
- = $e->{container}{id};
- }
-
- # Closing containers
- elsif (($e->{change} && $e->{change} eq "close"
- || $e->{change} && $e->{change} eq "floating"
- && $e->{container}{type} eq "floating_con")
- && exists $info{paper_ws}{$info{focused_ws}}) {
- # We just drop the column here. When the focus change event
- # comes in, that part of the loop is responsible for pulling
- # in another container as a replacement, if necessary.
- my $cols = $info{paper_ws}{$info{focused_ws}}{cols};
- my $i = first { $cols->[$_] == $e->{container}{id} }
- 0..$#$cols;
- splice $cols->@*, $i, 1;
+ normalise_ws_cols();
}
# Other container changes
- elsif ($e->{change} && $e->{change} eq "focus"
- && $e->{container} && $e->{container}{type} eq "con"
- && exists $info{paper_ws}{$info{focused_ws}}
- && !$info{paper_ws}{$info{focused_ws}}{monocle}) {
- # Change of window focus on one of our monitored workspaces.
- # Update which column we think is focused.
- my $ws = $info{paper_ws}{$info{focused_ws}};
- $ws->{focused_col} = $e->{container}{id};
-
- # If the change of focus was triggered by a container closing,
- # we might need to pull in a container.
- if ($ws->{ncols} > $ws->{cols}->@*
- && ($ws->{off_left}->@* || $ws->{off_right}->@*)) {
- my $cols = $ws->{cols};
- my $i = first { $cols->[$_] eq $e->{container}{id} }
- 0..$#$cols;
- my $mid_i = floor @$cols/2;
- if ($ws->{off_left}->@*
- && ($i < $mid_i || !$ws->{off_right}->@*)) {
- with_ignored_events {
- my $pulled = pop $ws->{off_left}->@*;
- $wmipc->cmd(("focus left")x$i,
- show_con($pulled), "move left",
- ("focus right")x$i);
- unshift @$cols, $pulled;
- $ws->{focused_col} = $cols->[$i];
- };
- } elsif ($ws->{off_right}->@*
- && ($i >= $mid_i || !$ws->{off_left}->@*)) {
- with_ignored_events {
- my $j = $#$cols - $i;
- my $pulled = pop $ws->{off_right}->@*;
- $wmipc->cmd(("focus right")x$j,
- show_con($pulled), ("focus left")x$j);
- push @$cols, $pulled;
- $ws->{focused_col} = $cols->[$i+1];
- };
- }
- }
- } elsif ($e->{change} && $e->{change} eq "move") {
- update_paper_ws_cols();
+ elsif ($e->{change} && exists $info{paper_ws}{$info{focused_ws}}
+ && ($e->{change} eq "close"
+ || $e->{change} eq "focus"
+ && $e->{container} && $e->{container}{type} eq "con"
+ || $e->{change} eq "floating"
+ && $e->{container}{type} eq "floating_con")) {
+ # Generally we seek to update $info{paper_ws} with the
+ # information we receive by subscription, but in some cases we
+ # can't be sure of what has happened.
+ # For example, as we don't maintain a representation of the
+ # whole tree, on a change=move event, we don't know where the
+ # container has gone. Or a focus change might be due to a new
+ # container, in which case we might need to push one off.
+ normalise_ws_cols();
}
# Ticks
@@ -190,6 +141,10 @@ unless (fork // warn "couldn't fork monitoring loop") {
# Workspace changes
elsif ($e->{change} && $e->{change} eq "focus" && $e->{current}) {
$info{focused_ws} = $e->{current}{id};
+ # Must normalise in case containers have moved to or from here
+ # in our absence.
+ normalise_ws_cols()
+ if exists $info{paper_ws}{$info{focused_ws}};
} elsif ($e->{change} && $e->{change} eq "init" && $e->{current}
&& grep $_ eq $e->{current}{name}, @all_workspaces) {
$info{paper_ws}{$e->{current}{id}}
@@ -338,36 +293,77 @@ sub clear_caffeinated {
kill USR1 => $i3status;
}
-sub update_paper_ws_cols {
- my @trees = $wmipc->get_tree;
- while (@trees) {
- foreach my $node ((shift @trees)->{nodes}->@*) {
- if ($node->{type} eq "workspace"
- && grep $_ eq $node->{name}, @all_workspaces) {
- my $entry = $info{paper_ws}{$node->{id}}
- //= { off_left => [], off_right => [] };
+sub sync_cols {
+ my ($node, $entry) = @_;
- # Here we assume that the containers for the columns are
- # directly below the type=workspace node. That won't be true
- # if workspace_layout is not configured to 'default'.
- foreach my $child_id ($node->{focus}->@*) {
- my $child_node
- = first { $_->{id} == $child_id } $node->{nodes}->@*;
- $entry->{focused_col} = $child_id, last
- if $child_node->{type} eq "con";
- }
+ # Here we assume that the containers for the columns are directly below
+ # the type=workspace node. That won't be true if workspace_layout is not
+ # configured to 'default'.
+ foreach my $child_id ($node->{focus}->@*) {
+ my $child_node = first { $_->{id} == $child_id } $node->{nodes}->@*;
+ $entry->{focused_col} = $child_id, last
+ if $child_node->{type} eq "con";
+ }
+ $entry->{cols} = [];
+ foreach my $child_node ($node->{nodes}->@*) {
+ push $entry->{cols}->@*, $child_node->{id}
+ if $child_node->{type} eq "con";
+ }
+}
- $entry->{cols} = [];
- foreach my $child_node ($node->{nodes}->@*) {
- push $entry->{cols}->@*, $child_node->{id}
- if $child_node->{type} eq "con";
+sub normalise_ws_cols {
+ my $ws = $info{paper_ws}{$info{focused_ws}};
+ my $floating_focus;
+ my @trees = $wmipc->get_tree;
+ while (@trees) {
+ for ((shift @trees)->{nodes}->@*) {
+ if ($_->{id} == $info{focused_ws}) {
+ sync_cols($_ => $ws);
+ my $first_focus = $_->{focus}->[0];
+ $floating_focus = ! grep $_ == $first_focus, $ws->{cols}->@*;
+ last;
+ }
+ unshift @trees, $_
+ }
+ }
+ my $cols = $ws->{cols};
+ with_ignored_events {
+ $wmipc->cmd("focus tiling");
+ while (1) {
+ my $i = first { $cols->[$_] eq $ws->{focused_col} } 0..$#$cols;
+ my $mid_i = floor @$cols/2;
+ # Pull column in if there are too few columns but some available.
+ if ($ws->{ncols} > @$cols
+ && ($ws->{off_left}->@* || $ws->{off_right}->@*)) {
+ if ($ws->{off_left}->@*
+ && ($i < $mid_i || !$ws->{off_right}->@*)) {
+ my $pulled = pop $ws->{off_left}->@*;
+ $wmipc->cmd(("focus left")x$i,
+ show_con($pulled), "move left",
+ ("focus right")x$i);
+ unshift @$cols, $pulled;
+ $ws->{focused_col} = $cols->[$i];
+ } elsif ($ws->{off_right}->@*
+ && ($i >= $mid_i || !$ws->{off_left}->@*)) {
+ my $j = $#$cols - $i;
+ my $pulled = pop $ws->{off_right}->@*;
+ $wmipc->cmd(("focus right")x$j,
+ show_con($pulled), ("focus left")x$j);
+ push @$cols, $pulled;
+ $ws->{focused_col} = $cols->[$i+1];
}
- $entry->{ncols} = max 2, scalar $entry->{cols}->@*;
- } elsif (grep $_ eq "caffeinated", $node->{marks}->@*) {
- register_caffeinated($node);
}
- unshift @trees, $node;
+ # Push a column off if there are too many columns.
+ elsif (@$cols > $ws->{ncols}) {
+ my $pushed = $i < $mid_i ? pop @$cols : shift @$cols;
+ $wmipc->cmd(hide_con($pushed));
+ push $ws->{$i < $mid_i ? "off_right" : "off_left"}->@*,
+ $pushed;
+ }
+ # Number of columns is correct.
+ else { last }
}
+ $wmipc->cmd("focus floating") if $floating_focus;
}
}