package App::XScreenSaver::DBus::Saver; use Moo; use experimental 'signatures'; use curry; use Log::Any; use Try::Tiny; use App::XScreenSaver::DBus::SaverProxy; has reactor => ( is => 'ro', required => 1 ); has bus => ( is => 'lazy', builder => sub { Net::DBus->session() } ); has dbus_srv => ( is => 'lazy', builder => sub { shift->bus->get_service('org.freedesktop.DBus') }, ); has dbus_obj => ( is => 'lazy', builder => sub { shift->dbus_srv->get_object('/org/freedesktop/DBus') }, ); has service => ( is => 'lazy', builder => sub { # this is the service name shift->bus->export_service('org.freedesktop.ScreenSaver'); }, ); has paths => ( is => 'ro', default => sub { [qw(/ScreenSaver /org/freedesktop/ScreenSaver)] }, ); has log => ( is => 'lazy', builder => sub { Log::Any->get_logger } ); has _impls => ( is => 'rw' ); has _prod_id => ( is => 'rw' ); has _inhibits => ( is => 'rw', default => sub { +{} } ); sub start($self) { my $inhibit_cb = $self->curry::weak::inhibit; my $uninhibit_cb = $self->curry::weak::uninhibit; $self->_impls([ map { App::XScreenSaver::DBus::SaverProxy->new( $self->service, $_, $inhibit_cb, $uninhibit_cb, ) } $self->paths->@* ]); $self->_prod_id( $self->reactor->add_timeout( 60_000, Net::DBus::Callback->new( method => $self->curry::weak::prod_screensaver ), 0, ), ); $self->dbus_obj->connect_to_signal( 'NameOwnerChanged', $self->curry::weak::name_owner_changed, ); return; } sub inhibit($self,$name,$reason,$message) { my $cookie; do { $cookie = int(rand(2**31)) } until !exists $self->_inhibits->{$cookie}; my $sender = $message->get_sender; $self->_inhibits->{$cookie} = [ $name, $reason, $sender ]; $self->log->debugf( '<%s> (%s) stops screensaver for <%s> (cookie %d) - %d active', $name, $sender, $reason, $cookie, scalar(keys $self->_inhibits->%*), ); $self->reactor->toggle_timeout($self->_prod_id, 1); return $cookie; } sub uninhibit($self,$cookie,$message) { my $inhibit = delete $self->_inhibits->{$cookie} or return; my ($name, $reason, $sender) = @$inhibit; my $this_sender = $message->get_sender; $self->log->debugf( '<%s> (was %s, is %s) resumed screensaver for <%s> (cookie %d) - %d left', $name, $sender, $this_sender, $reason, $cookie, scalar(keys $self->_inhibits->%*), ); $self->reactor->toggle_timeout($self->_prod_id, 0) unless $self->_inhibits->%*; return; } sub name_owner_changed($self,$bus_name,$old,$new) { $self->log->tracef('<%s> changed from <%s> to <%s>', $bus_name, $old, $new); for my $cookie (sort keys $self->_inhibits->%*) { my ($name, $reason, $sender) = @{$self->_inhibits->{$cookie}}; # is this inhibit from that bus name? next unless $sender && $sender eq $bus_name; # did the bus owner just disconnect? next unless $old && !$new; # if so, remove the inhibit my $inhibit = delete $self->_inhibits->{$cookie}; $self->log->debugf( '<%s> (%s) disconnected from the bus (it stopped screensaver for <%s>, cookie %d) - %d left', $name, $bus_name, $reason, $cookie, scalar(keys $self->_inhibits->%*), ); } unless ($self->_inhibits->%*) { $self->reactor->toggle_timeout($self->_prod_id, 0); } } sub prod_screensaver($self) { $self->log->debug('prodding xscreensaver'); system(qw(xscreensaver-command -deactivate)); } 1;