summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2021-05-01 10:48:04 +0100
committerdakkar <dakkar@thenautilus.net>2021-05-01 10:48:57 +0100
commit06563b95754156d8803d7dae47d1737985886a55 (patch)
tree9f7ff5b6d5de88a74397ca041403ef55d7b79e90
parentname, bump year & version (diff)
downloadxscreensaver-dbus-06563b95754156d8803d7dae47d1737985886a55.tar.gz
xscreensaver-dbus-06563b95754156d8803d7dae47d1737985886a55.tar.bz2
xscreensaver-dbus-06563b95754156d8803d7dae47d1737985886a55.zip
pod
-rw-r--r--lib/App/XScreenSaver/DBus.pm39
-rw-r--r--lib/App/XScreenSaver/DBus/InhibitSleep.pm61
-rw-r--r--lib/App/XScreenSaver/DBus/Saver.pm103
-rw-r--r--lib/App/XScreenSaver/DBus/SaverProxy.pm11
-rwxr-xr-xscripts/xscreensaver-dbus45
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<reactor>
+
+the event loop
+
+=cut
has reactor => (
is => 'lazy',
builder => sub { Net::DBus::Reactor->main() },
);
+=attr C<inhibit_sleep>
+
+instance of L<< C<App::XScreenSaver::DBus::InhibitSleep> >>.
+
+=cut
+
has inhibit_sleep => (
is => 'lazy',
builder => sub { App::XScreenSaver::DBus::InhibitSleep->new() },
);
+=attr C<saver>
+
+instance of L<< C<App::XScreenSaver::DBus::Saver> >>.
+
+=cut
+
has saver => (
is => 'lazy',
builder => sub($self) {
@@ -22,8 +48,21 @@ has saver => (
},
);
+=attr C<log>
+
+a logger
+
+=cut
+
has log => ( is => 'lazy', builder => sub { Log::Any->get_logger } );
+=method C<run>
+
+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<bus>
+
+the DBus system bus
+
+=cut
has bus => ( is => 'lazy', builder => sub { Net::DBus->system() } );
+
+=attr C<logind_srv>
+
+the (e)logind DBus service
+
+=cut
+
has logind_srv => (
is => 'lazy',
builder => sub { shift->bus->get_service('org.freedesktop.login1') },
);
+
+=attr C<logind_obj>
+
+the (e)logind DBus object
+
+=cut
+
has logind_obj => (
is => 'lazy',
builder => sub { shift->logind_srv->get_object('/org/freedesktop/login1') },
);
+=attr C<inhibit_fd>
+
+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<log>
+
+a logger
+
+=cut
+
has log => ( is => 'lazy', builder => sub { Log::Any->get_logger } );
+=method C<start>
+
+starts listening to the C<PrepareForSleep> 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<reactor>
+
+the event loop
+
+=cut
has reactor => ( is => 'ro', required => 1 );
+
+=attr C<bus>
+
+the DBus session bus
+
+=cut
+
has bus => ( is => 'lazy', builder => sub { Net::DBus->session() } );
+
+=attr C<dbus_srv>
+
+the DBus manager DBus service
+
+=cut
+
has dbus_srv => (
is => 'lazy',
builder => sub { shift->bus->get_service('org.freedesktop.DBus') },
);
+
+=attr C<dbus_obj>
+
+the DBus manager DBus object
+
+=cut
+
has dbus_obj => (
is => 'lazy',
builder => sub { shift->dbus_srv->get_object('/org/freedesktop/DBus') },
);
+=attr C<service>
+
+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<paths>
+
+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<log>
+
+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<start>
+
+Exports our object to the session bus, and starts listening for
+C<NameOwnerChanged> 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<Net::DBus::ObjectProxy> >>, but
+specialised for this application, and with a hack to allow L<<
+C<App::XScreenSaver::DBus::Saver> >> 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<the "idle inhibition" DBus
+service|https://people.freedesktop.org/~hadess/idle-inhibition-spec/index.html>
+and using L<the "inhibitor locks" feature of
+logind|https://www.freedesktop.org/wiki/Software/systemd/inhibit/>.
+
+=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<xscreensaver-systemd>, a
+stripped-down version of L<a program initially developed by Martin
+Lucina|https://github.com/mato/xscreensaver-systemd>, which handles
+the logind interaction. Both of those versions link to F<libsystemd>,
+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<xscreensaver-systemd> 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