summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2024-04-11 21:11:12 +0800
committerSean Whitton <spwhitton@spwhitton.name>2024-04-11 21:11:15 +0800
commitacad4445b81048140a7d658ac43f8a0a457750ad (patch)
treeb7a5dc5d29bec34f95f6e9f2e0519ccaa44e8432
parentdb13b64fa0ab8c691bd8a2075612bac5d9568613 (diff)
downloaddotfiles-acad4445b81048140a7d658ac43f8a0a457750ad.tar.gz
i3status-wrapper: replace IPC::Shareable & Local::Desktop::WMIPC
-rw-r--r--perl5/Local/Desktop.pm7
-rw-r--r--perl5/Local/Desktop/WMIPC.pm61
-rwxr-xr-xscripts/desktop/i3status-wrapper497
-rwxr-xr-xscripts/desktop/sway-ftp-master2
4 files changed, 246 insertions, 321 deletions
diff --git a/perl5/Local/Desktop.pm b/perl5/Local/Desktop.pm
index 0460447e..b61f9270 100644
--- a/perl5/Local/Desktop.pm
+++ b/perl5/Local/Desktop.pm
@@ -20,7 +20,6 @@ package Local::Desktop;
use 5.028;
use strict;
use warnings;
-use lib "$ENV{HOME}/src/dotfiles/perl5";
use Carp;
use JSON;
@@ -30,14 +29,18 @@ use File::Spec::Functions "rel2abs";
use Exporter "import";
use File::Copy;
use List::Util "first", "any";
-use Local::Desktop::WMIPC;
our @EXPORT = qw(
+ $wmipc wmipc
select_wallpaper_files
ensure_resize_for_current_outputs
resize_for_current_outputs
pick_random_wallpapers );
+`sh -c "command -v i3-msg"`;
+our $wmipc = $? == 0 ? "i3-msg" : "swaymsg";
+sub wmipc { system $wmipc, "-q", join "; ", @_ }
+
my $output_re = qr/ ([0-9]+)x([0-9]+)\+([0-9]+)\+([0-9]+) /;
=head select_wallpaper_files(@files)
diff --git a/perl5/Local/Desktop/WMIPC.pm b/perl5/Local/Desktop/WMIPC.pm
deleted file mode 100644
index effa7797..00000000
--- a/perl5/Local/Desktop/WMIPC.pm
+++ /dev/null
@@ -1,61 +0,0 @@
-package Local::Desktop::WMIPC;
-use 5.036;
-
-# Copyright (C) 2024 Sean Whitton
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or (at
-# your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-use IO::Socket::UNIX;
-use parent "IO::Socket::UNIX";
-use Exporter "import";
-use overload "<>" => sub { shift->recv };
-use JSON;
-use Encode "encode";
-
-our @EXPORT_OK = ();
-
-sub new ($class, $socket = $ENV{SWAYSOCK} || $ENV{I3SOCK}) {
- bless IO::Socket::UNIX->new(Type => SOCK_STREAM, Peer => $socket)
- => $class;
-}
-
-sub send ($self, $type, $payload = "") {
- $payload = encode "UTF-8", $payload;
- my $head = pack "A6ll", "i3-ipc", length $payload, $type;
- $self->SUPER::send($head.$payload);
- return $self->recv;
-}
-
-sub recv ($self) {
- my $buf;
- $self->SUPER::recv($buf, 14);
- length $buf or die "WMIPC socket EOF";
- my $len = (unpack "A6ll", $buf)[1];
- $self->SUPER::recv($buf, $len);
- return decode_json $buf;
-}
-
-sub subscribe ($self, @event_types) {
- my $reply = $self->send(2, encode_json \@event_types);
- $reply->{success} or die "WMIPC subscription failed";
-}
-
-sub cmd ($self, @cmds) { $self->send(0, join "; ", @cmds) }
-
-sub get_tree { shift->send(4) }
-sub get_workspaces { shift->send(1) }
-
-sub send_tick ($self, $payload = "") { $self->send(10, $payload) }
-
-1;
diff --git a/scripts/desktop/i3status-wrapper b/scripts/desktop/i3status-wrapper
index 201cdd7e..22b78bd0 100755
--- a/scripts/desktop/i3status-wrapper
+++ b/scripts/desktop/i3status-wrapper
@@ -20,14 +20,14 @@
use 5.032;
use strict;
use warnings;
-use lib "$ENV{HOME}/src/dotfiles/perl5";
use JSON;
use IO::Pipe;
-use IPC::Shareable ":lock";
-use Local::Desktop::WMIPC;
+use AnyEvent;
+use AnyEvent::I3 ":all";
use Sys::Hostname;
use POSIX "floor", "mkfifo";
+use Fcntl;
use File::Basename "basename", "dirname";
use File::Spec::Functions "catfile";
use List::Util qw(first min max zip);
@@ -44,17 +44,19 @@ unless ($i3status) {
exec "i3status";
}
-tie my %info, "IPC::Shareable", undef, { destroy => 1 };
+my (%paper_ws, $focused_ws, %col_rows, $caffeinated_id, $caffeinated_name);
-my $wmipc = Local::Desktop::WMIPC->new;
+my $wm_ipc_socket = $ENV{SWAYSOCK} || $ENV{I3SOCK};
+my $wmipc = AnyEvent::I3->new($wm_ipc_socket);
+$wmipc->connect->recv or die "couldn't connect to WM IPC socket";
sub with_ignored_events (&) {
- $wmipc->send_tick("i3status-wrapper-ign");
+ $wmipc->send_tick("i3status-wrapper-ign")->recv;
$_[0]->();
- $wmipc->send_tick("i3status-wrapper-unign");
+ $wmipc->send_tick("i3status-wrapper-unign")->recv;
}
sub for_each_node (&) {
- my @trees = $wmipc->get_tree;
+ my @trees = $wmipc->get_tree->recv;
while (@trees) {
foreach my $node ((shift @trees)->{nodes}->@*) {
$_[0]->($node);
@@ -70,165 +72,133 @@ my @all_workspaces = (
"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));
-
- # Determine the initial state -- the WM might just have been reloaded.
- # Move any previously-hidden containers to a fresh workspace for perusal.
+my $have_pending = AnyEvent->condvar;
+my (@pending_events, @pending_msgs);
- tied(%info)->lock;
- my @old_ids;
- for ($wmipc->get_workspaces->@*) {
- $info{focused_ws} = $_->{id} if $_->{focused};
- push @old_ids, $1 if $_->{name} =~ /\A\*(\d+)\*\z/;
- }
- if (@old_ids) {
- fresh_workspace(go => 1);
- $wmipc->cmd(
- map("[con_id=$_] move container workspace current, floating disable",
- @old_ids),
- "focus child");
- }
- for_each_node {
- my $node = shift;
- if ($node->{type} eq "workspace"
- && grep $_ eq $node->{name}, @all_workspaces) {
- my $entry = $info{paper_ws}{$node->{id}}
- //= { name => $node->{name}, off_left => [], off_right => [],
- last_dir => 1 };
- sync_cols($node => $entry);
- $entry->{ncols} = max 2, scalar $entry->{cols}->@*;
- } elsif (grep $_ eq "caffeinated", $node->{marks}->@*) {
- register_caffeinated($node);
+(basename $wm_ipc_socket) =~ /\d[\d.]*\d/;
+my $cmdpipe = catfile dirname($wm_ipc_socket), "i3status-wrapper.$&.pipe";
+-e and unlink for $cmdpipe;
+mkfifo $cmdpipe, 0700 or die "mkfifo $cmdpipe failed: $!";
+
+# Hold the pipe open with a writer that won't write anything.
+open(my $cmdpipe_w, ">", $cmdpipe), sleep
+ unless fork // die "couldn't fork: $!";
+
+open my $cmdpipe_r, "<", $cmdpipe;
+my $cmdpipe_reader = AnyEvent->io(
+ fh => $cmdpipe_r, poll => "r", cb => sub {
+ # There are a few cases where we can handle the command by only
+ # updating data structures, but for simplicity, always handle commands
+ # outside of the event loop.
+ push @pending_msgs, scalar <$cmdpipe_r>;
+ $have_pending->send;
+ });
+
+my $ignore_events;
+sub queue_event { push @pending_events, shift; $have_pending->send }
+$wmipc->subscribe({
+ tick => sub {
+ my $payload = shift->{payload};
+ $ignore_events = 1 if $payload eq "i3status-wrapper-ign";
+ $ignore_events = 0 if $payload eq "i3status-wrapper-unign";
+ },
+
+ window => sub {
+ return if $ignore_events;
+ my $e = shift; state $last_e;
+
+ # New containers: have to read two events to find out whether it's
+ # just a floating dialog that we'll ignore.
+ if ($last_e) {
+ undef $last_e;
+ queue_event $e unless $e->{change} && $e->{change} eq "floating";
+ } elsif ($e->{change} && $e->{change} eq "new"
+ && exists $paper_ws{$focused_ws}) {
+ $last_e = $e;
}
- };
- tied(%info)->unlock;
-
- # Now loop forever reading events, assuming no exceptions.
-
- eval {
- while (my $e = <$events>) {
- state $last_e;
- tied(%info)->lock;
-
- # New containers
- if ($last_e && $last_e->{change} && $last_e->{change} eq "new") {
- normalise_ws_cols()
- unless $e->{change} && $e->{change} eq "floating";
- undef $last_e;
- } elsif ($e->{change} && $e->{change} eq "new"
- && exists $info{paper_ws}{$info{focused_ws}}) {
- # We have to go round the loop once more to find out if it's
- # just a floating dialog that we'll ignore.
- $last_e = $e;
- } elsif ($e->{change} && exists $info{paper_ws}{$info{focused_ws}}
- && $e->{change} eq "floating"
- && $e->{container}{type} ne "floating_con") {
- # A container stopped floating -- it's as though it's new.
- normalise_ws_cols();
- kill USR1 => $i3status;
- }
- # Other container changes
- elsif ($e->{change} && exists $info{paper_ws}{$info{focused_ws}}
- && ($e->{change} eq "close"
- || $e->{change} eq "focus" && !$e->{current}
- || $e->{change} eq "move"
- && $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();
- kill USR1 => $i3status;
- }
-
- # Ticks
- elsif ($e->{payload} && $e->{payload} eq "i3status-wrapper-ign") {
- # Ignore everything until tick telling us to unignore.
- # Forked child that sent the ignore is responsible for
- # updating data structures in the meantime.
- while (my $next = <$events>) {
- last if $next->{payload}
- && $next->{payload} eq "i3status-wrapper-unign";
- }
+ # Mark changes -- can handle these without leaving event processing.
+ elsif ($e->{change} && $e->{change} eq "mark") {
+ if (grep $_ eq "caffeinated", $e->{container}{marks}->@*) {
+ register_caffeinated($e->{container});
+ } elsif ($caffeinated_id
+ && $caffeinated_id == $e->{container}{id}) {
+ clear_caffeinated();
}
+ }
- # 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}};
- kill USR1 => $i3status;
- } elsif ($e->{change} && $e->{change} eq "init" && $e->{current}
- && grep $_ eq $e->{current}{name}, @all_workspaces) {
- $info{paper_ws}{$e->{current}{id}}
- = { name => $e->{current}{name}, ncols => 2, cols => [],
- off_left => [], off_right => [], last_dir => 1 };
- } elsif ($e->{change} && $e->{change} eq "rename"
- && exists $info{paper_ws}{$e->{current}{id}}) {
- $info{paper_ws}{$e->{current}{id}}{name}
- = $e->{current}{name};
- kill USR1 => $i3status;
- } elsif ($e->{change} && $e->{change} eq "empty"
- && $e->{current}) {
- delete $info{paper_ws}{$e->{current}{id}};
+ # Other container changes we need to handle outside of any callback.
+ elsif ($e->{change} && exists $paper_ws{$focused_ws}
+ && (# A container stopped floating: it's as though it's new.
+ $e->{change} eq "floating"
+ && $e->{container}{type} ne "floating_con"
+
+ || $e->{change} eq "close" || $e->{change} eq "focus"
+
+ || $e->{change} eq "move"
+ && $e->{container} && $e->{container}{type} eq "con"))
+ { queue_event $e }
+ },
+
+ workspace => sub {
+ my $e = shift;
+ if ($ignore_events || !$e->{change}) {
+ return;
+ } elsif ($e->{change} eq "focus" && $e->{current}) {
+ $focused_ws = $e->{current}{id};
+ # If this is one of our workspaces, then we must normalise:
+ # containers might have moved to or from here in our absence.
+ if (exists $paper_ws{$focused_ws}) {
+ queue_event $e;
+ } else {
+ # Update status bar display.
kill USR1 => $i3status;
}
-
- # Mark changes
- elsif ($e->{change} && $e->{change} eq "mark") {
- if (grep $_ eq "caffeinated", $e->{container}{marks}->@*) {
- register_caffeinated($e->{container});
- } elsif ($info{caffeinated_id}
- and $info{caffeinated_id} == $e->{container}{id}) {
- clear_caffeinated();
- }
- }
-
- tied(%info)->unlock;
+ } elsif ($e->{change} eq "init" && $e->{current}
+ && grep $_ eq $e->{current}{name}, @all_workspaces) {
+ $paper_ws{$e->{current}{id}}
+ = { name => $e->{current}{name}, ncols => 2, cols => [],
+ off_left => [], off_right => [], last_dir => 1 };
+ } elsif ($e->{change} eq "rename"
+ && exists $paper_ws{$e->{current}{id}}) {
+ $paper_ws{$e->{current}{id}}{name} = $e->{current}{name};
+ kill USR1 => $i3status;
+ } elsif ($e->{change} eq "empty" && $e->{current}) {
+ delete $paper_ws{$e->{current}{id}};
+ kill USR1 => $i3status;
}
- };
-
- # Give up if there's a decoding error. We can't ignore the problem
- # because we don't want our ideas regarding what workspaces there are, and
- # whether anything is caffeinated, to get out of sync.
- #
- # The user can use the WM's "reload" command to restart this loop.
- $@ and wsbuttons("yes"), clear_caffeinated();
+ },
+})->recv->{success} or die "couldn't subscribe to window manager events";
+
+# Determine the initial state -- the WM might just have been reloaded.
+# Move any previously-hidden containers to a fresh workspace for perusal.
+my @old_ids;
+for ($wmipc->get_workspaces->recv->@*) {
+ $focused_ws = $_->{id} if $_->{focused};
+ push @old_ids, $1 if $_->{name} =~ /\A\*(\d+)\*\z/;
}
-
-my $wm_ipc_socket = $ENV{SWAYSOCK} || $ENV{I3SOCK};
-(basename $wm_ipc_socket) =~ /\d[\d.]*\d/;
-my $cmdpipe = catfile dirname($wm_ipc_socket), "i3status-wrapper.$&.pipe";
--e and unlink for $cmdpipe;
-
-unless (fork // warn "couldn't fork command pipe reader") {
- mkfifo $cmdpipe, 0700 or die "mkfifo $cmdpipe failed: $!";
- open my $cmdpipe_r, "<", $cmdpipe;
-
- # Hold the pipe open with a writer that won't write anything.
- open my $cmdpipe_w, ">", $cmdpipe;
-
- while (my $cmd = <$cmdpipe_r>) {
- tied(%info)->lock;
- process_msg($cmd);
- tied(%info)->unlock;
- }
+if (@old_ids) {
+ fresh_workspace(go => 1);
+ cmd(map("[con_id=$_] move container workspace current, floating disable",
+ @old_ids),
+ "focus child");
}
+for_each_node {
+ my $node = shift;
+ if ($node->{type} eq "workspace"
+ && grep $_ eq $node->{name}, @all_workspaces) {
+ my $entry = $paper_ws{$node->{id}}
+ //= { name => $node->{name},
+ off_left => [], off_right => [], last_dir => 1 };
+ sync_cols($node => $entry);
+ $entry->{ncols} = max 2, scalar $entry->{cols}->@*;
+ } elsif (grep $_ eq "caffeinated", $node->{marks}->@*) {
+ register_caffeinated($node);
+ }
+};
$pipe->reader;
-# Following based on Michael Stapelberg's sample i3status-wrapper script.
-
my $hostname = hostname;
my $username = $ENV{LOGNAME} || $ENV{USER} || getpwuid($<);
my $hostinfo
@@ -242,74 +212,90 @@ print scalar <$pipe>;
wsbuttons("no");
-# Read lines forever, ignore a comma at the beginning if it exists.
-while (my ($statusline) = (<$pipe> =~ /^,?(.*)/)) {
- # If there is a decoding error, just skip this line, to minimise status
- # bar freezes. This should be fine here because this filtering loop is in
- # itself stateless. It's only if the decoding error involves newlines in
- # the wrong places, or similar, that this skip could cause us to produce
- # invalid output.
- my $blocks = eval { decode_json $statusline } // next;
-
- tied(%info)->lock(LOCK_SH);
-
- if ($info{focused_ws}
- && $info{paper_ws} && keys $info{paper_ws}->%* > 1) {
- my @disp;
- my @keys = sorted_paper_ws();
- foreach my $key (@keys) {
- push @disp,
- sprintf +($info{focused_ws} == $key ? "<b>%s</b>" : "%s"),
- ws_name($info{paper_ws}{$key}{name})
-
+# Basic idea here from Michael Stapelberg's sample i3status-wrapper.
+my $i3status_wrapper = AnyEvent->io(
+ fh => $pipe, poll => "r", cb => sub {
+
+ # If there is a decoding error then we just skip this line, as it's
+ # not worth crashing this script over that. It should be fine to do
+ # this here because this filtering loop is in itself stateless.
+ # It's only if the decoding error involves newlines in wrong places,
+ # or similar, that this skip could cause us to produce invalid output.
+ my ($statusline) = (<$pipe> =~ /^,?(.*)/);
+ my $blocks = eval { decode_json $statusline } // next;
+
+ if ($focused_ws && keys %paper_ws > 1) {
+ my @disp;
+ foreach my $key (sorted_paper_ws()) {
+ push @disp,
+ sprintf +($focused_ws == $key ? "<b>%s</b>" : "%s"),
+ ws_name($paper_ws{$key}{name})
+ }
+ unshift @$blocks, { name => "ws", markup => "pango",
+ full_text => join " ", @disp };
}
- unshift @$blocks,
- { name => "ws", markup => "pango", full_text => join " ", @disp };
- }
- if ($info{focused_ws} && exists $info{paper_ws}{ $info{focused_ws} }) {
- sub nwin { join " ", ("\x{2021}")x$_[0] }
+ if ($focused_ws && exists $paper_ws{$focused_ws}) {
+ sub nwin { join " ", ("\x{2021}")x$_[0] }
+
+ my $ws = $paper_ws{$focused_ws};
+ my $left = $ws->{off_left}->@*;
+ my $right = $ws->{off_right}->@*;
- my $ws = $info{paper_ws}{ $info{focused_ws} };
- my $left = $ws->{off_left}->@*;
- my $right = $ws->{off_right}->@*;
+ my $disp = sprintf "<b>%s</b>",
+ $ws->{monocle} ? "\x{2020}" : nwin($ws->{ncols});
+ $disp = sprintf "%s %s", nwin($left), $disp if $left;
+ $disp = sprintf "%s %s", $disp, nwin($right) if $right;
- my $disp = sprintf "<b>%s</b>",
- $ws->{monocle} ? "\x{2020}" : nwin($ws->{ncols});
- $disp = sprintf "%s %s", nwin($left), $disp if $left;
- $disp = sprintf "%s %s", $disp, nwin($right) if $right;
+ unshift @$blocks,
+ { name => "cols", markup => "pango", full_text => $disp };
+ }
unshift @$blocks,
- { name => "cols", markup => "pango", full_text => $disp };
+ {
+ name => "caffeinated",
+ full_text => "Caffeinated: " . $caffeinated_name }
+ if $caffeinated_name;
+
+ unshift @$blocks, $hostinfo;
+
+ print encode_json($blocks) . ",\n";
+ });
+
+# Start main loop.
+for (;;) {
+ $have_pending->recv;
+ $have_pending = AnyEvent::condvar;
+
+ if (@pending_events) {
+ # Generally we would like to update %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. Currently,
+ # if we can't handle it within the callback, then we always normalise.
+ normalise_ws_cols();
+ kill USR1 => $i3status;
+ @pending_events = ();
}
-
- unshift @$blocks,
- {
- name => "caffeinated",
- full_text => "Caffeinated: " . $info{caffeinated_name} }
- if $info{caffeinated_name};
-
- tied(%info)->unlock;
-
- unshift @$blocks, $hostinfo;
-
- print encode_json($blocks) . ",\n";
+ process_msg(shift @pending_msgs) while @pending_msgs;
}
sub wsbuttons {
return unless $ENV{XDG_CURRENT_DESKTOP} eq "sway";
- $wmipc->cmd("bar bar-0 workspace_buttons $_[0]");
+ cmd("bar bar-0 workspace_buttons $_[0]");
}
sub register_caffeinated {
- $info{caffeinated_id} = $_[0]->{id};
- $info{caffeinated_name} = $_[0]->{name};
+ $caffeinated_id = $_[0]->{id};
+ $caffeinated_name = $_[0]->{name};
kill USR1 => $i3status;
}
sub clear_caffeinated {
- undef $info{caffeinated_id};
- undef $info{caffeinated_name};
+ undef $caffeinated_id;
+ undef $caffeinated_name;
kill USR1 => $i3status;
}
@@ -328,19 +314,19 @@ sub sync_cols {
foreach my $child_node ($node->{nodes}->@*) {
next unless $child_node->{type} eq "con";
push $entry->{cols}->@*, $child_node->{id};
- $info{col_rows}{$child_node->{id}} = $child_node->{nodes}->@*;
+ $col_rows{$child_node->{id}} = $child_node->{nodes}->@*;
}
}
sub normalise_ws_cols {
- my $ws = $info{paper_ws}{$info{focused_ws}};
+ my $ws = $paper_ws{$focused_ws};
my $floating_focus;
my $old_cols = $ws->{cols};
my $old_i = shift // first { $old_cols->[$_] == $ws->{focused_col} }
0..$#$old_cols;
for_each_node {
my $node = shift;
- if ($node->{id} == $info{focused_ws}) {
+ if ($node->{id} == $focused_ws) {
sync_cols($node => $ws);
my $first_focus = $node->{focus}->[0];
$floating_focus = ! grep $_ == $first_focus, $ws->{cols}->@*;
@@ -360,18 +346,18 @@ 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) {
+ if ($ws->{focused_col} && $col_rows{$ws->{focused_col}}
+ && $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] }) {
+ if ($i < $#$cols && !$col_rows{ @$cols[$i+1] }) {
push @cmds, "move right";
- delete $info{col_rows}{$ws->{focused_col}};
+ delete $col_rows{$ws->{focused_col}};
$ws->{focused_col} = $cols->[$i] = node_first_child($cols->[$i]);
- } elsif ($i > 0 && !$info{col_rows}{ @$cols[$i-1] }) {
+ } elsif ($i > 0 && !$col_rows{ @$cols[$i-1] }) {
push @cmds, "move left";
- delete $info{col_rows}{$ws->{focused_col}};
+ delete $col_rows{$ws->{focused_col}};
$ws->{focused_col} = $cols->[$i] = node_first_child($cols->[$i]);
}
}
@@ -422,7 +408,7 @@ sub normalise_ws_cols {
for (@to_pull) {
push @cmds, show_con(@$_[0]);
next unless @$_[1];
- push @cmds, $info{col_rows}{@$_[1]}
+ push @cmds, $col_rows{@$_[1]}
? "move left"
: "swap container with con_id @$_[1]";
}
@@ -437,7 +423,7 @@ sub normalise_ws_cols {
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 @cmds, "move right" if @$_[1] && $col_rows{@$_[1]};
}
push @$cols, @pulled;
@@ -451,7 +437,7 @@ sub normalise_ws_cols {
}
$ws->{focused_col} = $cols->[$old_i];
- push @cmds, "focus child" if $info{col_rows}{$ws->{focused_col}};
+ push @cmds, "focus child" if $col_rows{$ws->{focused_col}};
}
# Push columns off if there are too many columns.
# This should never change which container is focused.
@@ -474,16 +460,16 @@ sub normalise_ws_cols {
}
if (@cmds) {
push @cmds, "focus floating" if $floating_focus;
- with_ignored_events { $wmipc->cmd("focus tiling", @cmds) }
+ with_ignored_events { cmd("focus tiling", @cmds) }
}
}
sub process_msg {
my $cmd = shift;
- my $ws = $info{paper_ws}{$info{focused_ws}};
+ my $ws = $paper_ws{$focused_ws};
my $cols = $ws->{cols};
- my $col_rows = $info{col_rows}{$ws->{focused_col}};
+ my $rows = $col_rows{$ws->{focused_col}};
my $i = first { $cols->[$_] == $ws->{focused_col} } 0..$#$cols;
my $mv = sub {
@@ -491,12 +477,12 @@ sub process_msg {
if (@$cols > $j >= 0) {
if ($move) {
# This does not trigger any events.
- $wmipc->cmd(
+ 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");
+ cmd($j > $i ? "focus right" : "focus left");
}
} elsif ($move && $ws->{monocle}) {
if ($j > $i && $ws->{off_right}->@*) {
@@ -511,8 +497,8 @@ sub process_msg {
push $ws->{off_left}->@*, $pushed;
if ($move) {
- if ($col_rows || @$cols) {
- push @cmds, $col_rows
+ if ($rows || @$cols) {
+ push @cmds, $rows
? "move left"
: "swap container with con_id @$cols[-1]";
push @cmds, "focus right";
@@ -525,12 +511,12 @@ sub process_msg {
}
} else {
$ws->{focused_col} = $pulled;
- push @cmds, "move right" if $col_rows;
+ push @cmds, "move right" if $rows;
push @$cols, $pulled;
- push @cmds, "focus child" if $info{col_rows}{$pulled};
+ push @cmds, "focus child" if $col_rows{$pulled};
}
- with_ignored_events { $wmipc->cmd(@cmds, hide_con($pushed)) };
+ with_ignored_events { cmd(@cmds, hide_con($pushed)) };
kill USR1 => $i3status;
} elsif ($j == -1 && $ws->{off_left}->@*) {
my $pushed = pop @$cols;
@@ -541,7 +527,7 @@ sub process_msg {
if ($move) {
if (@$cols) {
- push @cmds, "move right" if $col_rows;
+ push @cmds, "move right" if $rows;
push @cmds, "focus left";
my $tem = shift @$cols;
unshift @$cols, $tem, $pulled;
@@ -549,17 +535,17 @@ sub process_msg {
unshift @$cols, $pulled;
}
} else {
- if ($col_rows) {
+ if ($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};
+ push @cmds, "focus child" if $col_rows{$pulled};
}
- with_ignored_events { $wmipc->cmd(@cmds, hide_con($pushed)) };
+ with_ignored_events { cmd(@cmds, hide_con($pushed)) };
kill USR1 => $i3status;
}
$ws->{last_dir} = $j > $i ? 1 : -1;
@@ -598,31 +584,31 @@ sub process_msg {
});
} elsif ($cmd =~ /^absorb_expel ?(left|right)?$/) {
my $dir = $1 eq "right" ? 1 : -1;
- if ($col_rows > 1) { # expel
+ if ($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");
+ cmd(sprintf "move %s", $dir > 0 ? "right" : "left");
$ws->{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, "splitv" unless $col_rows{$pulled};
push @cmds, "focus right", "move left";
- with_ignored_events { $wmipc->cmd(@cmds) };
+ with_ignored_events { 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, "move right" if $rows;
+ push @cmds, "splitv" unless $col_rows{$pulled};
push @cmds, "focus left", "move right";
- with_ignored_events { $wmipc->cmd(@cmds) };
+ with_ignored_events { cmd(@cmds) };
normalise_ws_cols();
} elsif ($i == $#$cols && $dir < 0
|| $#$cols > $i > 0
@@ -630,10 +616,10 @@ sub process_msg {
push @cmds, $dir > 0
? ("focus right", "splitv", "focus left")
: ("focus left", "splitv", "focus right")
- unless $info{col_rows}{ @$cols[$i+$dir] };
+ unless $col_rows{ @$cols[$i+$dir] };
push @cmds, $dir > 0 ? "move right" : "move left";
- with_ignored_events { $wmipc->cmd(@cmds) };
+ with_ignored_events { cmd(@cmds) };
normalise_ws_cols($ws->{off_left}->@* && $dir > 0
|| $ws->{off_right}->@* && $dir < 0
? min($#$cols, max 0, $i+$dir) : $i);
@@ -651,18 +637,17 @@ sub process_msg {
my ($move, $dir) = (!!$1, $2);
$move && ensure_disable_monocle($ws);
my @keys = sorted_paper_ws();
- my $k = first { $keys[$_] == $info{focused_ws} } 0..$#keys;
+ my $k = first { $keys[$_] == $focused_ws } 0..$#keys;
if ($dir eq "next" && $k < $#keys || $dir eq "prev" && $k > 0) {
my @cmds = "workspace $dir";
- $info{focused_ws} = $keys[$dir eq "next" ? $k+1 : $k-1];
+ $focused_ws = $keys[$dir eq "next" ? $k+1 : $k-1];
if ($move) {
push @cmds, show_con($ws->{focused_col});
push @cmds, "move right"
- if $info{col_rows}
- { $info{paper_ws}{$info{focused_ws}}{focused_col} };
- push @cmds, "focus child" if $col_rows;
+ if $col_rows{ $paper_ws{$focused_ws}{focused_col} };
+ push @cmds, "focus child" if $rows;
}
- $wmipc->cmd(@cmds);
+ cmd(@cmds);
}
}
}
@@ -685,7 +670,7 @@ sub fresh_workspace {
# 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.
- ensure_disable_monocle(my $ws = $info{paper_ws}{$info{focused_ws}});
+ ensure_disable_monocle(my $ws = $paper_ws{$focused_ws});
# We need to ensure that the monitoring loop doesn't process the move
# event before it knows about the workspace change. Otherwise, that
@@ -706,7 +691,7 @@ sub fresh_workspace {
push @cmds, show_con($ws->{focused_col}) if $opts{send};
push @cmds, "workspace back_and_forth" unless $opts{go};
- $wmipc->cmd(@cmds);
+ cmd(@cmds);
}
$next_free_workspace
}
@@ -731,9 +716,9 @@ sub compact_workspaces {
my $workspace = $all_workspaces[$i++];
$opts{leave_gap}
- and $next == $info{focused_ws}
+ and $next == $focused_ws
and $gap_workspace = $all_workspaces[$i++];
- my $next_name = $info{paper_ws}{$next}{name};
+ my $next_name = $paper_ws{$next}{name};
next if $next_name eq $workspace;
my $pair = [$next, $workspace];
ws_num($next_name) > ws_num($workspace)
@@ -742,12 +727,11 @@ sub compact_workspaces {
}
with_ignored_events {
- $wmipc->cmd(
- map sprintf("rename workspace %s to %s",
- $info{paper_ws}{$_->[0]}{name}, $_->[1]),
+ cmd(map sprintf("rename workspace %s to %s",
+ $paper_ws{$_->[0]}{name}, $_->[1]),
@pairs)
};
- $info{paper_ws}{$_->[0]}{name} = $_->[1] for @pairs;
+ $paper_ws{$_->[0]}{name} = $_->[1] for @pairs;
$opts{leave_gap} and $gap_workspace
}
@@ -775,11 +759,12 @@ sub ensure_disable_monocle {
}
sub sorted_paper_ws {
- sort { ws_num($info{paper_ws}{$a}{name})
- <=> ws_num($info{paper_ws}{$b}{name}) }
- keys $info{paper_ws}->%*
+ sort { ws_num($paper_ws{$a}{name}) <=> ws_num($paper_ws{$b}{name}) }
+ keys %paper_ws
}
+sub cmd { $wmipc->command(join "; ", @_)->recv }
+
sub hide_con {
# Enable floating in order to preserve any rows the container might have.
# Otherwise, Sway subsumes the rows to the hidden workspace and the
diff --git a/scripts/desktop/sway-ftp-master b/scripts/desktop/sway-ftp-master
index 5cb3f6a6..0a36feee 100755
--- a/scripts/desktop/sway-ftp-master
+++ b/scripts/desktop/sway-ftp-master
@@ -14,8 +14,6 @@ use lib "$ENV{HOME}/src/dotfiles/perl5";
use JSON;
use Local::Desktop;
-sub wmipc { system "swaymsg", "-q", join ", ", @_ }
-
sub walk_tree (&$) {
my ($pred, $tree) = @_;
$pred->($tree) and return $tree;