From 06563b95754156d8803d7dae47d1737985886a55 Mon Sep 17 00:00:00 2001 From: dakkar Date: Sat, 1 May 2021 10:48:04 +0100 Subject: pod --- lib/App/XScreenSaver/DBus.pm | 39 +++++++++++ lib/App/XScreenSaver/DBus/InhibitSleep.pm | 61 ++++++++++++++++-- lib/App/XScreenSaver/DBus/Saver.pm | 103 ++++++++++++++++++++++++++---- lib/App/XScreenSaver/DBus/SaverProxy.pm | 11 ++++ scripts/xscreensaver-dbus | 45 +++++++++++++ 5 files changed, 241 insertions(+), 18 deletions(-) diff --git a/lib/App/XScreenSaver/DBus.pm b/lib/App/XScreenSaver/DBus.pm index 10c87fd..c8b6cf5 100644 --- a/lib/App/XScreenSaver/DBus.pm +++ b/lib/App/XScreenSaver/DBus.pm @@ -2,19 +2,45 @@ package App::XScreenSaver::DBus; use Moo; use experimental 'signatures'; use Net::DBus::Reactor; +use Log::Any; use App::XScreenSaver::DBus::InhibitSleep; use App::XScreenSaver::DBus::Saver; +# VERSION +# ABSTRACT: main application class + +=head1 SYNOPSIS + + use App::XScreenSaver::DBus; + App::XScreenSaver::DBus->new->run; + +=attr C + +the event loop + +=cut has reactor => ( is => 'lazy', builder => sub { Net::DBus::Reactor->main() }, ); +=attr C + +instance of L<< C >>. + +=cut + has inhibit_sleep => ( is => 'lazy', builder => sub { App::XScreenSaver::DBus::InhibitSleep->new() }, ); +=attr C + +instance of L<< C >>. + +=cut + has saver => ( is => 'lazy', builder => sub($self) { @@ -22,8 +48,21 @@ has saver => ( }, ); +=attr C + +a logger + +=cut + has log => ( is => 'lazy', builder => sub { Log::Any->get_logger } ); +=method C + +registers the DBus services and runs the event loop; this method does +not return + +=cut + sub run($self) { $self->inhibit_sleep->start(); $self->saver->start(); diff --git a/lib/App/XScreenSaver/DBus/InhibitSleep.pm b/lib/App/XScreenSaver/DBus/InhibitSleep.pm index 44f84bc..b419f50 100644 --- a/lib/App/XScreenSaver/DBus/InhibitSleep.pm +++ b/lib/App/XScreenSaver/DBus/InhibitSleep.pm @@ -4,31 +4,82 @@ use experimental 'signatures'; use curry; use Net::DBus; use Log::Any; +# VERSION +# ABSTRACT: implements the logind "inhibitor locks" protocol + +=head1 SYNOPSIS + + use Net::DBus::Reactor; + use App::XScreenSaver::DBus::InhibitSleep; + my $is = App::XScreenSaver::DBus::InhibitSleep->new; + $is->start; + + Net::DBus::Reactor->new->run; + +=attr C + +the DBus system bus + +=cut has bus => ( is => 'lazy', builder => sub { Net::DBus->system() } ); + +=attr C + +the (e)logind DBus service + +=cut + has logind_srv => ( is => 'lazy', builder => sub { shift->bus->get_service('org.freedesktop.login1') }, ); + +=attr C + +the (e)logind DBus object + +=cut + has logind_obj => ( is => 'lazy', builder => sub { shift->logind_srv->get_object('/org/freedesktop/login1') }, ); +=attr C + +the file descriptor that logind gives us when we ask for a lock; we +close it to release the lock + +=cut + has inhibit_fd => ( is => 'rwp' ); +=attr C + +a logger + +=cut + has log => ( is => 'lazy', builder => sub { Log::Any->get_logger } ); +=method C + +starts listening to the C signal from (e)logind, and +takes the lock + +=cut + sub start($self) { $self->logind_obj->connect_to_signal( 'PrepareForSleep', - $self->curry::weak::going_to_sleep, + $self->curry::weak::_going_to_sleep, ); - $self->inhibit(); + $self->_inhibit(); return; } -sub inhibit($self) { +sub _inhibit($self) { return if $self->inhibit_fd; $self->_set_inhibit_fd( $self->logind_obj->Inhibit( @@ -41,7 +92,7 @@ sub inhibit($self) { return; } -sub going_to_sleep($self,$before) { +sub _going_to_sleep($self,$before) { if ($before) { $self->log->debug('locking'); system(qw(xscreensaver-command -suspend)); @@ -51,7 +102,7 @@ sub going_to_sleep($self,$before) { else { $self->log->debug('woken up'); system(qw(xscreensaver-command -deactivate)); - $self->inhibit(); + $self->_inhibit(); } return; } diff --git a/lib/App/XScreenSaver/DBus/Saver.pm b/lib/App/XScreenSaver/DBus/Saver.pm index 6ae6e69..369e8bd 100644 --- a/lib/App/XScreenSaver/DBus/Saver.pm +++ b/lib/App/XScreenSaver/DBus/Saver.pm @@ -5,18 +5,64 @@ use curry; use Log::Any; use Try::Tiny; use App::XScreenSaver::DBus::SaverProxy; +# VERSION +# ABSTRACT: implements the "idle inhibition" protocol + +=head1 SYNOPSIS + + use Net::DBus::Reactor; + use App::XScreenSaver::DBus::InhibitSleep; + + my $reactor = Net::DBus::Reactor->new; + my $s = App::XScreenSaver::DBus::Saver->new(reactor => $reactor); + $s->start; + + $reactor->run; + +=attr C + +the event loop + +=cut has reactor => ( is => 'ro', required => 1 ); + +=attr C + +the DBus session bus + +=cut + has bus => ( is => 'lazy', builder => sub { Net::DBus->session() } ); + +=attr C + +the DBus manager DBus service + +=cut + has dbus_srv => ( is => 'lazy', builder => sub { shift->bus->get_service('org.freedesktop.DBus') }, ); + +=attr C + +the DBus manager DBus object + +=cut + has dbus_obj => ( is => 'lazy', builder => sub { shift->dbus_srv->get_object('/org/freedesktop/DBus') }, ); +=attr C + +the DBus service we export + +=cut + has service => ( is => 'lazy', builder => sub { @@ -24,21 +70,49 @@ has service => ( shift->bus->export_service('org.freedesktop.ScreenSaver'); }, ); + +=attr C + +the paths at which we export our DBus object + +there's two of them because different applications expect this object +at different paths + +=cut + has paths => ( is => 'ro', default => sub { [qw(/ScreenSaver /org/freedesktop/ScreenSaver)] }, ); +=attr C + +a logger + +=cut + 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 { +{} } ); +=method C + +Exports our object to the session bus, and starts listening for +C events. + +Those events are emitted when a client attaches or detaches from the +bus. A client may die before releasing the idle inhibition, so we want +to be notified when that happens, and release that inhibition. + +=cut + sub start($self) { - my $inhibit_cb = $self->curry::weak::inhibit; - my $uninhibit_cb = $self->curry::weak::uninhibit; + my $inhibit_cb = $self->curry::weak::_inhibit; + my $uninhibit_cb = $self->curry::weak::_uninhibit; + # export to dbus $self->_impls([ map { App::XScreenSaver::DBus::SaverProxy->new( $self->service, @@ -52,21 +126,21 @@ sub start($self) { $self->reactor->add_timeout( 60_000, Net::DBus::Callback->new( - method => $self->curry::weak::prod_screensaver + method => $self->curry::weak::_prod_screensaver ), - 0, + 0, # this means "don't call my yet" ), ); $self->dbus_obj->connect_to_signal( 'NameOwnerChanged', - $self->curry::weak::name_owner_changed, + $self->curry::weak::_name_owner_changed, ); return; } -sub inhibit($self,$name,$reason,$message) { +sub _inhibit($self,$name,$reason,$message) { my $cookie; do { $cookie = int(rand(2**31)) @@ -79,12 +153,14 @@ sub inhibit($self,$name,$reason,$message) { '<%s> (%s) stops screensaver for <%s> (cookie %d) - %d active', $name, $sender, $reason, $cookie, scalar(keys $self->_inhibits->%*), ); + + # that 1 means "start calling me" $self->reactor->toggle_timeout($self->_prod_id, 1); return $cookie; } -sub uninhibit($self,$cookie,$message) { +sub _uninhibit($self,$cookie,$message) { my $inhibit = delete $self->_inhibits->{$cookie} or return; my ($name, $reason, $sender) = @$inhibit; @@ -95,13 +171,14 @@ sub uninhibit($self,$cookie,$message) { $name, $sender, $this_sender, $reason, $cookie, scalar(keys $self->_inhibits->%*), ); + # if there's no more inhibitions, stop prodding the screen saver $self->reactor->toggle_timeout($self->_prod_id, 0) unless $self->_inhibits->%*; return; } -sub name_owner_changed($self,$bus_name,$old,$new) { +sub _name_owner_changed($self,$bus_name,$old,$new) { $self->log->tracef('<%s> changed from <%s> to <%s>', $bus_name, $old, $new); @@ -109,7 +186,7 @@ sub name_owner_changed($self,$bus_name,$old,$new) { 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? + # did the bus client just disconnect? next unless $old && !$new; # if so, remove the inhibit @@ -121,12 +198,12 @@ sub name_owner_changed($self,$bus_name,$old,$new) { ); } - unless ($self->_inhibits->%*) { - $self->reactor->toggle_timeout($self->_prod_id, 0); - } + # if there's no more inhibitions, stop prodding the screen saver + $self->reactor->toggle_timeout($self->_prod_id, 0) + unless $self->_inhibits->%*; } -sub prod_screensaver($self) { +sub _prod_screensaver($self) { $self->log->debug('prodding xscreensaver'); system(qw(xscreensaver-command -deactivate)); } diff --git a/lib/App/XScreenSaver/DBus/SaverProxy.pm b/lib/App/XScreenSaver/DBus/SaverProxy.pm index d098045..f4773bc 100644 --- a/lib/App/XScreenSaver/DBus/SaverProxy.pm +++ b/lib/App/XScreenSaver/DBus/SaverProxy.pm @@ -5,6 +5,17 @@ use experimental 'signatures'; # this is the interface name use Net::DBus::Exporter qw(org.freedesktop.ScreenSaver); use parent 'Net::DBus::Object'; +# VERSION +# ABSTRACT: proxy dbus object + +=head1 DESCRIPTION + +This is functionally the same as L<< C >>, but +specialised for this application, and with a hack to allow L<< +C >> to access the sender of the +message. + +=cut dbus_method('Inhibit',['string','string'],['uint32']); dbus_method('UnInhibit',['uint32'],[]); diff --git a/scripts/xscreensaver-dbus b/scripts/xscreensaver-dbus index 5e188f2..98e61c9 100755 --- a/scripts/xscreensaver-dbus +++ b/scripts/xscreensaver-dbus @@ -3,6 +3,51 @@ use strict; use warnings; use Log::Any::Adapter Stdout => ( log_level => 'debug' ); use App::XScreenSaver::DBus; +# VERSION +# PODNAME: xscreensaver-dbus +# ABSTRACT: tie xscreensaver into dbus $|++; App::XScreenSaver::DBus->new->run; + +=head1 SYNOPSIS + + xscreensaver & + xscreensaver-dbus > ~/xscreensaver-dbus.log & + +=head1 DESCRIPTION + +This program will ensure that xscreensaver locks the screen before +your laptop goes to sleep, and will prevent the screen saver from +running while video is playing. + +It does so by implementing L +and using L. + +=head1 WHY? + +If you use xscreensaver, you may have noticed that it doesn't +integrate very well with all the new & fancy desktop features. + +jwz has resisted adding this kind of integration to the base program, +saying (sensibly) that they would bloat the code and introduce +potential security holes. + +From version 5.43, xscreensaver includes F, a +stripped-down version of L, which handles +the logind interaction. Both of those versions link to F, +which you may not have if you use elogind instead of the full systemd. + +So I wrote this. + +=head1 REFERENCES + +=for :list +* Martin Lucina F https://github.com/mato/xscreensaver-systemd +* the Debian bug where the above program is first mentioned https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=781961 +* "Is there a decent way to inhibit screensavers in linux?" on StackOverflow https://stackoverflow.com/questions/460140/is-there-a-decent-way-to-inhibit-screensavers-in-linux +* the systemd "Inhibitor Locks" documentation https://www.freedesktop.org/wiki/Software/systemd/inhibit/ +* the "idle inhibition" spec https://people.freedesktop.org/~hadess/idle-inhibition-spec/index.html -- cgit v1.2.3