Reputation: 1077
What's the better cross-DE way to disable the screensaver in Linux? I found something here but it's only for gnome-screensaver. I'm wondering if there's any way to simulate a keystroke or some X.Org API to disable screensaver activation.
Upvotes: 7
Views: 5510
Reputation: 8332
I know this question is old, but there is a more modern answer to it: now most DE on linux use dbus to communicate, and you can use this:
in shell, using kde tools:
qdbus org.freedesktop.ScreenSaver /ScreenSaver org.freedesktop.ScreenSaver.Inhibit "myapps" "because I want it"
this dbus call will return a cookie (a number) that will be needed to uninhibit the screesaver:
qdbus org.freedesktop.ScreenSaver /ScreenSaver org.freedesktop.ScreenSaver.UnInhibit $COOKIE
In python, it will be something like
import dbus
bus = dbus.SessionBus()
saver = bus.get_object('org.freedesktop.ScreenSaver', '/ScreenSaver')
saver_interface = dbus.Interface(saver, dbus_interface='org.freedesktop.ScreenSaver')
# now we can inhibit the screensaver
cookie=saver_interface.Inhibit("myapps", "because I want it")
# we can also restore it
saver_interface.UnInhibit(cookie)
Upvotes: 4
Reputation: 5502
TLDR; there aren't any cross-DE ways to inhibit the screensaver with pure Python. However the question also asked about doing so with Linux (presumably from the shell). To be clear, there aren't any particularly good cross-DE ways to do so in Linux, but at least there is potential. You probably want to use the hackish xdg-screensaver
shell script.
This is a topic I've spent some time on. I wasn't happy with any of the solutions out there, so after playing around with some homebrew tools, I ended up modifying the caffeine project to suit my needs and have been using it happily ever since. I'll summarize some of what I've learned in the process.
In case you aren't familiar with it, I'd recommend checking out caffeine (Launchpad PPA @ https://launchpad.net/~caffeine-developers/+archive/ubuntu/ppa).
The caffeine project mostly consists of the following scripts: caffeine-screensaver
caffeine
caffeine-indicator
caffeinate
caffeine
and caffeine-indicator
are written in Python 3 whereas caffeine-screensaver
is a rebranded xdg-screensaver
written in shell script.
The first half of ccpizza's answer is great as it is cross-DE and very similar to caffeine's implementation. I haven't had any issues with xdg-screensaver zombie processes but I'm sure it could be remedied if I better knew the use-case or the conditions under which it was occurring. Just a FYI that running xdg-screensaver suspend ROOT_WINDOW_ID
will background a tracking process and leave it running indefinitely (until it is resumed).
If you're interested in implementing a truly cross-DE solution then I would strongly recommend examining the aforementioned xdg-screensaver
/caffeine-screensaver
. The many forks of lightsOn are ripe as well.
xdg-screensaver
detects the desktop environment (KDE, Gnome, XFCE, LXDE, xscreensaver, or gnome-screensaver) and acts accordingly. For the xdg-screensaver suspend
command, it runs xset -dpms
to disable DPMS, DE-specific commands, then watches the supplied window ID for it's duration. If the window disappears, the resume command is run and xdg-screensaver exits. The xdg-screensaver resume
command consists of xset +dpms
(only if DPMS was originally enabled) then the DE-specific commands.
Note that xdg-screensaver will only track the supplied window ID if xprop is available (it's part of x11-utils, so it should be). To track the window, it backgrounds xprop -id WINDOW_ID -spy
to monitor the window for changes. It stores the PID of the aforemention process along with window ID in a lockfile to reference upon suspend and resume. The original state of DPMS is referenced in a separate lockfile. The lockfiles should ensure there are no duplicate processes.
The script is a complete mess, so I empathize with anyone unfortunate enough to use it, but I haven't encountered any issues. You probably don't want to reinvent the wheel so if you are creating something for public consumption then I'd recommend you just co-opt it. If you're creating something for personal use, then just dig through it and use the commands specific to your DE.
ccpizza suggesting using subprocess.Popen('xwininfo -root | grep xwininfo | cut -d" " -f4', stdout=subprocess.PIPE, shell=True).stdout.read().strip()
to get the desktop's root window ID. Using the desktop's root window ID with xdg-screensaver suspend
will disable the desktop's screensaver indefinitely. Using a specific window's ID will disable the desktop's screensaver for the duration of the window.
To determine the active window ID, you can run the following:
xprop -root _NET_ACTIVE_WINDOW | awk -F '[ ,]' '{print $5}'
To determine the root window ID, you can run the following:
xwininfo -root | awk '/^xwininfo: Window id: / {print $4}'
To determine the screensaver timeout, you can run the following:
xset q | awk '/^ timeout: / {print $2}'
NOTE: the result will be 0 if the screensaver is disabled
To determine whether DPMS is enabled or disabled, you can run the following:
xset q | awk '/^ DPMS is / {print tolower($3)}'
There are pure Python options as well. The Xlib
module is great for this, as is ewmh
(which is a wrapper around Xlib).
To determine the active window ID with ewmh:
active_id = hex(ewmh.EWMH().getActiveWindow().id)
To determine the root window ID with ewmh:
root_id = hex(ewmh.EWMH().root.id)
To determine the active window ID with Xlib:
from Xlib.display import Display
from Xlib.X import AnyPropertyType
active_id = hex(Display().screen().root.get_full_property(Display().get_atom('_NET_ACTIVE_WINDOW'), AnyPropertyType).value[0])
To determine the root window ID with Xlib:
from Xlib.display import Display
root_id = hex(Display().screen().root.id)
To determine the screensaver timeout with Xlib:
from Xlib.display import Display
timeout = Display().get_screen_saver().timeout
Upvotes: 1
Reputation: 31666
I have been looking into this a while ago and finally ended up using xdg-screensaver
which I call via subprocess
.
import subprocess
def suspend_screensaver():
window_id = subprocess.Popen('xwininfo -root | grep xwininfo | cut -d" " -f4', stdout=subprocess.PIPE, shell=True).stdout.read().strip()
#run xdg-screensaver on root window
subprocess.call(['xdg-screensaver', 'suspend', window_id])
def resume_screensaver(window_id):
subprocess.Popen('xdg-screensaver resume ' + window_id, shell=True)
This is not ideal but apparently there is no other solution that would not involve messing around with DE-specific stuff like dbus
or gnome-screensaver-command
.
I don't really like the call to xwininfo
and wish there was a cleaner way but so far could not find anything better. Another issue with the xwininfo
approach is that it uses the id of the root window instead of the app window. Using the app window id instead of the root window would remove the need for the resume_screensaver
method since it would then resume as soon as the window is destroyed.
And if you want to simulate keystrokes here is a naive bash script I have been using for some time. It does require xdotool
which has to be installed separately.
#!/bin/bash
while :
do
sleep 200
nice -n 1 xdotool key shift
echo .
done
After having used the python solution above for over a year, it was found to occasionally create zombie processes and/or too many instances of xdg-screensaver
, so after digging around, I found a simpler alternative which is Gnome-specific, but works for me even in a non-Gnome DE (XFCE) since the core Gnome libraries are required by many GTK-based apps even if you don't have a Gnome desktop.
import subprocess
def suspend_screensaver():
'suspend linux screensaver'
proc = subprocess.Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled false', shell=True)
proc.wait()
def resume_screensaver():
'resume linux screensaver'
proc = subprocess.Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled true', shell=True)
proc.wait()
Upvotes: 5