From 14b917b5779e350a10db2397ff4da788a0945dcc Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sun, 14 Jan 2024 19:38:39 +0000 Subject: i3status-wrapper: improve & simplify normalising the number of cols --- scripts/desktop/i3status-wrapper | 206 +++++++++++++++++++-------------------- 1 file 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; } } -- cgit v1.2.3