use v5.40;
use feature 'class';
no warnings 'experimental::class';
use lib '/home/dakkar/.perlbrew/libs/perl-5.40.0/lib/perl5';
use Gtk2 -init;
class BatteryStats {
field $now :reader;
field $full :reader;
field $deriv :reader;
field $fraction :reader;
field $charging :reader;
field $is_full :reader;
field $time_now :reader;
field $time_full :reader;
sub get_battery_stats {
open my $fh,'<','/sys/class/power_supply/BAT1/uevent';
my %ret;
while (my $line=<$fh>) {
chomp $line;
my ($name,$value) = $line =~ m{\A POWER_SUPPLY_(.*?) = (.*) \z}x;
next unless $name;
$ret{lc($name)}= $ENV{"POWER_SUPPLY_$name"} || $value;
}
return \%ret;
}
ADJUST {
my $bs = get_battery_stats();
$now = $bs->{energy_now} // $bs->{charge_now};
$full = $bs->{energy_full} // $bs->{charge_full};
$deriv = $bs->{power_now} // $bs->{current_now};
$fraction = $now/$full;
$charging = lc($bs->{status}) eq 'charging';
$is_full = lc($bs->{status}) eq 'full';
if ($deriv && !$charging) {
$time_now = $now / $deriv;
$time_full = $full / $deriv;
}
};
}
class MainWindow {
field $window :reader;
field $bar;
ADJUST {
$window = Gtk2::Window->new('toplevel');
$window->set_accept_focus(0);
$window->set_decorated(0);
$window->set_has_frame(0);
$window->set_skip_pager_hint(1);
$window->set_skip_taskbar_hint(1);
$window->set_type_hint('utility');
$window->set_default_size(300,25);
$window->set_resizable(1);
$window->set_name('dakkar-battery');
$window->set_wmclass('dakkar-battery','dakkar-battery');
$bar = Gtk2::ProgressBar->new();
$bar->set_orientation('left-to-right');
$bar->set_name('dakkar-battery-bar');
$window->add($bar);
$window->signal_connect(delete_event => sub { Gtk2->main_quit });
};
sub _format_time($t) {
my $h=int($t);
my $m=int(($t-$h)*60);
return sprintf q{%dh%02d},$h,$m;
}
method update($bs) {
my @symbols;
my $time_estimate='';
my $percentage=sprintf '%.0f%%', 100*$bs->fraction;
if ($bs->deriv && !$bs->charging) {
$time_estimate = sprintf "%s/%s",
_format_time($bs->time_now),
_format_time($bs->time_full);
}
if ($bs->charging) {
push @symbols, "\x{1f50c}";
}
if (!$bs->is_full && $bs->fraction > 0.2) {
push @symbols, "\x{1f50b}";
}
elsif (!$bs->is_full) {
push @symbols, "\x{1faab}";
}
$bar->set_fraction($bs->fraction);
$bar->set_text(join "\x{2009}", grep { length } @symbols, $time_estimate, $percentage);
return 1;
};
method show_all() { $window->show_all }
};
class WarningWindow {
field $parent :param;
field $window;
field $shown = 0;
ADJUST {
$window = Gtk2::Dialog->new(
'LOW POWER', $parent,
[qw(modal destroy-with-parent)],
);
my $icon = Gtk2::Image->new_from_icon_name('dialog-warning', 'dialog');
$icon->set_pixel_size(256);
my $label = Gtk2::Label->new();
$label->set_markup(<<'EOF');
<span size="64pt" color="red">LOW POWER</span>
<span size="48pt" allow_breaks="true" insert_hyphens="true">Connect charger or suspend</span>
EOF
$window->get_content_area->add($icon);
$window->get_content_area->add($label);
$window->signal_connect(delete_event=>\&Gtk2::Widget::hide_on_delete);
};
method show() {
return if $shown;
$window->show_all();
$shown=1;
}
method hide() {
return unless $shown;
$window->hide_all();
$shown=0;
}
};
my $main_window = MainWindow->new();
my $warning = WarningWindow->new(parent=>$main_window->window);
Glib::Timeout->add(
1000, sub {
my $bs = BatteryStats->new();
if (!$bs->charging && !$bs->is_full && $bs->fraction < 0.05) {
$warning->show();
}
elsif ($bs->charging || $bs->is_full || $bs->fraction > 0.15) {
$warning->hide();
}
$main_window->update($bs);
},
);
$main_window->show_all;
Gtk2->main;