summaryrefslogtreecommitdiffstats
path: root/i3status/.bin/status
diff options
context:
space:
mode:
Diffstat (limited to 'i3status/.bin/status')
-rwxr-xr-xi3status/.bin/status276
1 files changed, 276 insertions, 0 deletions
diff --git a/i3status/.bin/status b/i3status/.bin/status
new file mode 100755
index 0000000..5674876
--- /dev/null
+++ b/i3status/.bin/status
@@ -0,0 +1,276 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use 5.010;
+
+use IO::Select;
+use IO::Socket::UNIX;
+use JSON::PP;
+
+my $i3status = get_i3status_handle();
+my $ipc = get_ipc_handle();
+$|++;
+say "[]";
+
+my $select = IO::Select->new;
+$select->add($i3status);
+$select->add($ipc);
+
+my $current_data;
+
+while (my @ready = $select->can_read) {
+ for my $fh (@ready) {
+ if ($fh == $i3status) {
+ handle_i3status(scalar <$fh>);
+ }
+ elsif ($fh == $ipc) {
+ handle_ipc(receive_ipc_message($fh));
+ }
+ else {
+ die "???";
+ }
+ }
+}
+
+sub get_i3status_handle {
+ open my $i3status, '-|', 'i3status';
+
+ print scalar(<$i3status>);
+ print scalar(<$i3status>);
+
+ return $i3status;
+}
+
+sub handle_i3status {
+ $_ = shift;
+ s/^,//;
+
+ my $data = decode_json($_);
+
+ my %line = map { $_->{name} => $_ } @$data;
+ my $line = [
+ grep {
+ $_->{name} eq 'wireless' || $_->{name} eq 'ethernet'
+ ? $_->{instance} ne $_->{full_text}
+ : $_->{name} ne 'load'
+ } @$data
+ ];
+
+ my %discharge_rate;
+ my %battery_index;
+
+ my $index = 0;
+ for my $item (@$line) {
+ if ($item->{name} eq 'wireless' || $item->{name} eq 'ethernet') {
+ my $iface = $item->{instance};
+ my ($up, $down) = map { net_rate($iface, $_) } 'rx', 'tx';
+ my $rate = sprintf("%6s/%6s", map { human_bytes($_) } $up, $down);
+ $item->{full_text} =~ s{^($iface:)}{$1 $rate};
+ $item->{color} =
+ $up + $down >= 512000 ? '#FF0000'
+ : $up + $down >= 51200 ? '#FFFF00'
+ : $up + $down >= 5120 ? '#00FF00'
+ : '#FFFFFF';
+ }
+ if ($item->{name} eq 'cpu_usage') {
+ my ($val) = $line{load}{full_text} * 100 / ncpu();
+ $item->{color} =
+ $val >= 50 ? '#FF0000'
+ : $val >= 25 ? '#FFFF00'
+ : $val >= 5 ? '#00FF00'
+ : '#FFFFFF';
+ }
+ if ($item->{name} eq 'battery') {
+ my ($state, $val) = $item->{full_text} =~ /(\w+) (\d+(?:\.\d+))%/;
+ my ($rate) = $item->{full_text} =~ /\(.* (.*)W\)/;
+ $rate *= -1 if defined($rate) && $state ne 'BAT';
+ $rate //= 0;
+
+ $discharge_rate{$item->{instance}} = $rate;
+ $battery_index{$item->{instance}} = $index;
+
+ $item->{full_text} =~ s/\((.*) (.*)W\)/\($1\)/;
+ $item->{full_text} =~ s/ \(\)//;
+ $item->{color} =
+ !defined($val) ? '#FFFFFF'
+ : $val >= 80 ? '#00FF00'
+ : $state ne 'BAT' ? '#FFFFFF'
+ : $val >= 40 ? '#FFFFFF'
+ : $val >= 15 ? '#FFFF00'
+ : $val >= 5 ? '#CD0000'
+ : '#FF0000';
+ }
+ $index++;
+ }
+
+ my @batteries = sort { $battery_index{$b} <=> $battery_index{$a} }
+ keys %battery_index;
+ for my $battery (@batteries) {
+ next unless $discharge_rate{$battery};
+ splice @$line, $battery_index{$battery} + 1, 0, {
+ name => 'battery_discharge',
+ full_text => sprintf("%0.2fW", $discharge_rate{$battery}),
+ color => (
+ $discharge_rate{$battery} < 6 ? '#FFFFFF'
+ : $discharge_rate{$battery} < 10 ? '#00FF00'
+ : $discharge_rate{$battery} < 14 ? '#FFFF00'
+ : '#FF0000'
+ ),
+ };
+ }
+
+ my $weather_file = "$ENV{HOME}/.cache/weather";
+ if (-r $weather_file && -f $weather_file) {
+ my $weather = slurp($weather_file);
+ chomp($weather);
+ my ($temp) = $weather =~ /\d+%.*?(-?\d+)C.*?-?\d+C/;
+ unshift @$line, {
+ name => 'weather',
+ full_text => $weather,
+ color => (
+ $temp >= 30 ? '#FF0000'
+ : $temp >= 25 ? '#FFFF00'
+ : $temp >= 20 ? '#00FF00'
+ : $temp >= 10 ? '#FFFFFF'
+ : $temp >= 0 ? '#CDCDFF'
+ : '#0000FF'
+ ),
+ };
+ }
+
+ chomp(my $brightness = `backlight get`);
+ $brightness = sprintf("%3d", $brightness);
+ splice @$line, -1, 0, {
+ name => 'brightness',
+ full_text => "\N{WHITE SUN WITH RAYS} $brightness%",
+ color => '#FFFFFF'
+ };
+
+ chomp(my $volume = `volume get`);
+ if ($volume eq 'mute') {
+ splice @$line, -1, 0, {
+ name => 'volume',
+ full_text => "\N{SPEAKER} ",
+ color => '#FFFFFF'
+ };
+ }
+ else {
+ $volume = sprintf("%3d", $volume);
+ splice @$line, -1, 0, {
+ name => 'volume',
+ full_text => "\N{SPEAKER WITH THREE SOUND WAVES} $volume%",
+ color => '#FFFFFF'
+ };
+ }
+
+ $current_data = $line;
+
+ show_status();
+}
+
+sub get_ipc_handle {
+ chomp(my $path = qx(i3 --get-socketpath));
+
+ my $sock = IO::Socket::UNIX->new(Peer => $path);
+ $sock->write(format_ipc_message(2, q{["window", "workspace"]}));
+ my ($type, $payload) = receive_ipc_message($sock);
+ die "Couldn't subscribe: ($type) $payload"
+ unless $type == 2 && decode_json($payload)->{success};
+
+ return $sock;
+}
+
+sub handle_ipc {
+ my ($type, $payload) = @_;
+ show_status();
+}
+
+sub format_ipc_message {
+ my ($type, $payload) = @_;
+ my $len = do { use bytes; length($payload) };
+ return "i3-ipc" . pack("LL", $len, $type) . $payload;
+}
+
+sub receive_ipc_message {
+ my ($sock) = @_;
+
+ $sock->read(my $magic, 6);
+ die "unknown response: $magic" unless $magic eq 'i3-ipc';
+ $sock->read(my $len, 4);
+ $len = unpack("L", $len);
+ $sock->read(my $type, 4);
+ $type = unpack("L", $type);
+ $sock->read(my $payload, $len);
+
+ return ($type, $payload);
+}
+
+sub show_status {
+ my @line = @$current_data;
+ unshift @line, { name => 'title', full_text => get_title() };
+ say "," . encode_json(\@line);
+}
+
+sub get_title {
+ my $node = find(decode_json(`i3-msg -t get_tree`));
+ if ($node->{window}) {
+ return $node->{name};
+ }
+ else {
+ return '-none-';
+ }
+}
+
+sub find {
+ my ($t) = @_;
+ if ($t->{focused}) {
+ return $t;
+ }
+
+ for my $subtree (@{ $t->{nodes} }, @{ $t->{floating_nodes} }) {
+ my $found = find($subtree);
+ return $found if $found;
+ }
+
+ return;
+}
+
+{
+ my %last_bytes;
+ sub net_rate {
+ my ($iface, $which) = @_;
+ my $stat_file = "/sys/class/net/$iface/statistics/${which}_bytes";
+ chomp(my $bytes = slurp($stat_file));
+ my $prev = $last_bytes{$iface}{$which};
+ $last_bytes{$iface}{$which} = $bytes;
+ return 0 unless defined $prev;
+ return $bytes - $prev;
+ }
+}
+
+sub ncpu {
+ scalar grep { !/^#/ } `lscpu -p=cpu`
+}
+
+sub human_bytes {
+ my ($bytes) = @_;
+ my @prefixes = ('', qw(k M G T));
+ my $prefix_index = 0;
+ while ($bytes > 512) {
+ $bytes /= 1024;
+ $prefix_index++;
+ }
+ my $prec = ($prefix_index == 0 ? 0 : 1);
+ return sprintf("%0.${prec}f%sB", $bytes, $prefixes[$prefix_index]);
+}
+
+sub slurp {
+ open my $fh, '<:encoding(UTF-8)', $_[0] or die "Couldn't open $_[0]: $!";
+ if (wantarray) {
+ <$fh>
+ }
+ else {
+ local $/;
+ <$fh>
+ }
+}