diff options
Diffstat (limited to 'i3status/.bin/status')
-rwxr-xr-x | i3status/.bin/status | 276 |
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> + } +} |