Is there a decent way to inhibit screensavers in linux? - screensaver

I'm looking for a decent, non-lame way to inhibit xscreensaver, kscreensaver, or gnome-screensaver, whichever might be running, preferably in a screensaver-agnostic manner, and it absolutely positively must execute fast.
I've read the xscreensaver FAQ.
I have a gtk based game program that's cranking out 30 frames/second while mixing several channels of audio, and since it's controlled by a joystick, sometimes "the" screensaver will kick in. I put "the" in quotes, because there are at least three different popular screensavers, xscreensaver, gnome-screensaver, and kscreensaver, each with their own unique and clunky methods by which an application might inhibit them.
Has anybody encapsulated the code to inhibit all of these into a fast chunk of code? Oh, and it has to be GPL compatible.
Currently my code simply whines piteously about the uncooperative screensaver developers if any screensaver is detected and the joystick is in use, and doesn't actually try to do anything other than advise the user to manually disable the screensaver, as the only other thing I can think to do is so incredibly ugly that I simply refuse to do it.
Just wondering if anybody else has run into this, and what they've done, and if they did anything, if it was as ugly as it seems to me it would have to be, or if there's some elegant solution out there... Seems like maybe synthesizing X events somehow to fool the screensaver into thinking there's some activity might do the trick in a universal way, but I'm really not sure how to do that (and hoping you wouldn't need to be root to do it.)
Any ideas?
Hmm, unfortunately, at least on Fedora core 8, this does not appear to work.
The xdg-screensaver script is there, and seems to be intended to work, it just doesn't actually work.
Once you do "xdg-screensaver suspend window-id", where window id is gotten from within the program via
xwindow_id = GDK_WINDOW_XWINDOW (GTK_WIDGET (widget)->window);
Or whether the window id is gotten via xprop, and xdg-screensaver run manually, two processes are created:
[scameron#zuul wordwarvi]$ ps -efa | grep xdg
scameron 4218 1 0 20:12 pts/2 00:00:00 /bin/sh /usr/bin/xdg-screensaver suspend 0x3a00004
scameron 4223 1 0 20:12 pts/2 00:00:00 /bin/sh /usr/bin/xdg-screensaver suspend 0x3a00004
scameron 4313 3151 0 20:15 pts/1 00:00:00 grep xdg
[scameron#zuul wordwarvi]$
And they never die, even after the program they are supposedly waiting for dies, and the screensaver is never re-enabled.
[scameron#zuul wordwarvi]$ xdg-screensaver status
disabled
[scameron#zuul wordwarvi]$ ls -ltr /tmp | grep xdg
-rw------- 1 scameron scameron 15 2009-01-20 20:12 xdg-screensaver-scameron--0.0
[scameron#zuul wordwarvi]$
Running xdg-screensaver resume window-id does not resume the screensaver.
To re-enable the screensaver, I have to manually kill them, and manually remove the files it leaves around in /tmp:
[scameron#zuul wordwarvi]$ kill 4218 4223
[scameron#zuul wordwarvi]$ rm /tmp/xdg-screensaver-scameron--0.0
[scameron#zuul wordwarvi]$ xdg-screensaver status
enabled
[scameron#zuul wordwarvi]$
So, good intentions, but doesn't seem to actually work.
No, of course not expecting to run it every frame, but don't want it causing hiccups when it does run, is all. With my thought of synthesizing X events, I was imagining it would be just often enough to make the screen saver think there was activity.
Looking at xdg-screensaver (which seems to be a shell script that ultimately just does a "wait" for my process -- cool) it seems to be made to do just what I want. I knew I couldn't be the only or first one to face this problem.

No, but yes...
There's no nice clean way to do this. In my opinion there should be a mechanism administrated by the X server, which both screensavers and interested applications can voluntarily use to negotiate suppression of any screensaver during the runtime of one or more programs. But no such mechanism yet exists to my knowledge. GNOME and KDE look to be implementing a DBUS approach to this problem, but in my opinion even if it becomes widespread (it isn't yet widespread enough to rely on it in 3rd party code) it's not the right approach.
However, xdg-screensaver is a FreeDesktop standardised shell script which you can run as a sub-process to control the screensaver. It controls most popular screensavers, and the OS vendor would be responsible for updating it/ maintaining it to work with newer screensavers or better ways of doing this in the future. Unlike many other kludges it will automatically re-enable the screensaver if your application crashes or exits via some route that forgets to call the re-enable code. See the manual page for details in how to use it.
As a GTK+ user probably the trickiest aspects of this for you would be creating the sub-process to run the shell script (if you haven't done this before you will want to find a tutorial about using fork + exec) and getting the XWindow ID of your application's main window to give to xdg-screensaver.
You ask that the code should be "fast". This makes me wonder if you're expecting to run it every frame - don't. The xdg-screensaver solution allows you to disable or renable the screensaver explicitly, rather than trying to suppress it once per frame or anything like that.

Try this: xdg-screensaver suspend [window id]
(see: http://portland.freedesktop.org/xdg-utils-1.0/xdg-screensaver.html)
a Bash script solution:
activ_win_id=`DISPLAY=:0.${display} xprop -root _NET_ACTIVE_WINDOW`
activ_win_id=${activ_win_id:40:9}
xdg-screensaver suspend $activ_win_id
It works for gnome-screensaver.
And there's the more complicated DBus "inhibit" method: https://people.gnome.org/~mccann/gnome-screensaver/docs/gnome-screensaver.html#gs-method-Inhibit
dbus-send --session \
--dest=org.gnome.ScreenSaver \
--type=method_call \
--print-reply \
--reply-timeout=20000 \
/org/gnome/ScreenSaver \
org.gnome.ScreenSaver.Inhibit \
[application name e.g. ""] \
[reason e.g."playing a game"] \
[non-zero-random-integer e.g. 123]

Edit 2019/12/04:
I went back to this and found out that dbus-send doesn't work properly now. The Inhibit method is released as soon as the D-Bus connection is released (which happens when dbus-send exits).
A good method (working with Gnome 3.34) is the following python script:
#!/usr/bin/python
# coding=utf-8
# Usage: gnome-inhibit <command-line>
# Example: gnome-inhibit mpv video.mp4
import subprocess
import dbus
import sys
import os
bus = dbus.SessionBus()
proxy = bus.get_object('org.freedesktop.ScreenSaver','/org/freedesktop/ScreenSaver')
iface = dbus.Interface(proxy, 'org.freedesktop.ScreenSaver')
cookie = iface.Inhibit(sys.argv[1], "gnome-inhibit")
print("Inhibiting screensaver (pid: %d)" % os.getpid())
print("Executing: %s" % subprocess.list2cmdline(sys.argv[1:]))
subprocess.check_call(sys.argv[1:])
Old answer that doesn't work anymore:
The current way (under Gnome 3.22 and possibly others such as KDE) seems to be this, which returns an uninhibition cookie as an uint32 :
dbus-send --session \
--dest=org.freedesktop.ScreenSaver \
--type=method_call \
--print-reply \
--reply-timeout=20000 \
/org/freedesktop/ScreenSaver \
org.freedesktop.ScreenSaver.Inhibit \
string:program string:reason
Then, to disable the inhibition :
dbus-send --session \
--dest=org.freedesktop.ScreenSaver \
--type=method_call \
--print-reply \
--reply-timeout=20000 \
/org/freedesktop/ScreenSaver \
org.freedesktop.ScreenSaver.UnInhibit \
uint32:<inhibit-cookie>
Here is a small script that disables the screensaver for a given program, then later restores it on exit.
(D-Feet has been especially helpful for introspecting the ever-changing D-Bus method names)

Movie-players typically disable screensavers. You could poke around the mplayer-code to see how they do it.
For regular X, they use XScreenSaverSuspend where supported.

It is not an entirely desktop-agnostic solution, but if core Gnome libraries are installed (many GTK-based apps require them) it could work on other desktop environments too:
Disable screensaver:
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
(Re)enable screensaver:
gsettings set org.gnome.desktop.screensaver idle-activation-enabled true

This 'dirty solution' works for me on Debian 10 + LXDE:
[ "$(cat ~/.xsessionrc | grep 'xset -dpms')" ] || echo 'xset -dpms' >> ~/.xsessionrc # disables DPMS (Energy Star) features
sed -i '/^timeout:/c timeout:\t3:00:00' ~/.xscreensaver # 3 hours should be enough to see a film without interruptions
# DOESN'T WORK: xset s off # turns the screen saver functions off

Install caffine-ng. Tray app. Totally customizable activate and ignore lists. Audio detection. Screensaver / Locker never goes off again when you don't want it to. EASY.

Related

How to properly start a Gnome-Shell extension via command line?

After copying myexentension#me.com folder to .../gnome-shell/extensions/ I'm executing this command on the terminal:
gnome-shell-extension-tool -e myexentension#me.com
Then, I restart my session with Alt + F2 and execute r, and everything works fine.
But can I start my extension only through the command line? Without Alt+F2+r? Without restarting my gnome-shell session?
According to some answers around the internet, sending SIGHUP to the gnome-shell process restarts it (i. e. killall -HUP gnome-shell), but I haven’t been able to find a clear source on this and couldn’t find the signal handling in the code. What I do know is that this should be exactly equivalent to Alt+F2 r:
busctl --user call org.gnome.Shell /org/gnome/Shell org.gnome.Shell Eval s 'Meta.restart("Restarting…")'
Because apart from a gettext call on the message, this is exactly what Alt+F2 r is bound to (see runDialog.js – search for _restart).
January 2022 update: Since Gnome 41, calling Eval is restricted and requires “unsafe mode” to be enabled, so by default this will no longer work. I’m not currently aware of a replacement for this particular usage.
Personally, I prefer a solid Alt+F2, r+Enter but maybe try disabling and enabling:
gnome-shell-extension-tool -d myexentension#me.com && gnome-shell-extension-tool -e myexentension#me.com
or
gnome-shell-extension-tool -r myexentension#me.com
Which may do the same thing. There's also gnome-shell-extension-prefs which you can use to do the same thing (and is typically hidden in Gnome for some reason).

shell: clean up leaked background processes which hang due to shared stdout/stderr

I need to run essentially arbitrary commands on a (remote) shell in ephemeral containers/VMs for a test execution engine. Sometimes these leak background processes which then cause the entire command to hang. This can be boiled down to this simple command:
$ sh -c 'sleep 30 & echo payload'
payload
$
Here the backgrounded sleep 30 plays the role of a leaked process (which in reality will be something like dbus-daemon) and the echo is the actual thing I want to run. The sleep 30 & echo payload should be considered as an atomic opaque example command here.
The above command is fine and returns immediately as the shell's and also sleep's stdout/stderr are a PTY. However, when capturing the output of the command to a pipe/file (a test runner wants to save everything into a log, after all), the whole command hangs:
$ sh -c 'sleep 30 & echo payload' | cat
payload
# ... does not return to the shell (until the sleep finishes)
Now, this could be fixed with some rather ridiculously complicated shell magic which determines the FDs of stdout/err from /proc/$$/fd/{1,2}, iterating over ls /proc/[0-9]*/fd/* and killing every process which also has the same stdout/stderr. But this involves a lot of brittle shell code and expensive shell string comparisons.
Is there a way to clean up these leaked background processes in a more elegant and simpler way? setsid does not help:
$ sh -c 'setsid -w sh -c "sleep 30 & echo payload"' | cat
payload
# hangs...
Note that process groups/sessions and killing them wholesale isn't sufficient as leaked processes (like dbus-daemon) often setsid themselves.
P.S. I can only assume POSIX shell or bash in these environments; no Python, Perl, etc.
Thank you in advance!
We had this problem with parallel tests in Launchpad. The simplest solution we had then - which worked well - was just to make sure that no processes share stdout/stdin/stderr (except ones where you actually want to hang if they haven't finished - e.g. the test workers themselves).
Hmm, having re-read this I cannot give you the solution you are after (use systemd to kill them). What we came up with is to simply ignore the processes but reliably not hang when the single process we were waiting for is done. Note that this is distinctly different from the pipes getting closed.
Another option, not perfect but useful, is to become a local reaper with prctl(2) and PR_SET_CHILD_SUBREAPER. This will allow you to be the parent of all the processes that would otherwise reparent to init. With this arrangement you could try to kill all the processes that have you as ppid. This is terrible but it's the closest best thing to using cgroups.
But note, that unless you are running this helper as root you will find that practical testing might spawn some setuid thing that will lurk and won't be killable. It's an annoying problem really.
Use script -qfc instead of sh -c.

How can you start a LaunchAgent for the first time without rebooting, when your code runs as a LaunchDaemon?

I have a LaunchDaemon. When it runs, it checks if SIMBL is installed. If SIMBL is not installed, it uses NSTask to run /usr/sbin/installer on the SIMBL.pkg.
SIMBL's postflight script then tries to run a launchctl load command to start SIMBL's LaunchAgent immediately:
sudo -u "$USER" -- /bin/launchctl load -F -S Aqua -D user "${LAUNCHD_PLIST}"
This fails, because my LaunchDaemon's NSTask environment doesn't have $USER set.
If I have my daemon detect the current user with the System Configuration framework and pass it to NSTask with setEnvironment, launchctl bugs out on me:
Bug: launchctl.c:2325 (23930):13: (dbfd = open(g_job_overrides_db_path, O_RDONLY | O_EXLOCK | O_CREAT, S_IRUSR | S_IWUSR)) != -1
I realize a daemon, by definition, should not operate in a user session. By the same token, Apple seems to recommend LaunchAgents as helper objects for LaunchDaemons, to do that user session work. Is there any way to get such an agent up and running immediately?
I have all the .plists in the right places (they start running after a reboot, the next time launchctl does its regular loading) so my first thought was to just tell launchctl to reload. But all the code to do that is commented out in launchctl.c:
// { "reload", reload_cmd, "Reload configuration files and/or directories" },
...
* In later versions of launchd, I hope to load everything in the first pass,
* then do the Bonjour magic on the jobs that need it, and reload them, but for now,
* I haven't thought through the various complexities of reloading jobs, and therefore
* launchd doesn't have reload support right now.
Oh how launchd drives me crazy....
To cut to the chase, after much study and experimentation, this is how I do it on 10.5+:
# If possible, tell launchd to start the LaunchAgent. This will fail on 10.4.
# $F[0] is the pid
# $F[1] is the username
# $F[2] is the first word of the command
ps -ww -A -opid,user,command | \
perl -nae 'if($F[2] =~ /\bloginwindow\b/) { system(
qq(launchctl bsexec $F[0] su $F[1] -c "launchctl load -w <your_plist>"))
}'
I have found no way to achieve this directly on 10.4. I cheat on 10.4 and just run the thing the LaunchAgent would have run, even though it has a GUI and you're not supposed to be able to do that (you can anyway in 10.4-10.6; you can't in 10.7). On 10.4, the LaunchAgent works correct after the next reboot.
The above code looks for loginwindow processes and uses bsexec to run commands in those contexts. Keep in mind that with Fast User Switching, there can be multiple contexts.
Some useful links:
Daemons and Services Programming Guide. You have to read it, but it won't actually answer any of the hard questions. But it will at least give you hints at where everything is located.
TN2083. This is a maddening document that raises as many questions as it answers, but is gospel and mandatory reading for anyone entering the abyss of launchd.
Starting/stopping a launchd agent for all users with GUI sessions. This has several other useful links and explanation.
IMO, launchd is one of the worst "great ideas" Apple has ever deployed. The idea is very useful, but the API is horrible.

Disable/Cancel Sleep Command on MacOSX

It seems to be impossible to completely disable the Sleep option in MacOSX so that a user cannot manually put the system to sleep.
Is there a way in Leopard (or even Snow Leopard) for AppleScript to catch the Sleep event and cancel it?
The answer to your problem is however a simple Apple-osx-Terminal command that prevents OSX to go idle:
$ pmset noidle
Preventing idle sleep (^C to exit)...
The only way to reset the no-sleeping is to issue a 'Control-C'.
See the man pages of pmset for more details.
This command has been deprecated in favor for
$ caffeinate -i
Which does roughly the same.
You can also run caffeinate for a pre-determined amount of time to prevent sleeping, say for 4 hours while you download something, and then run it in the background by adding & to it:
$ caffeinate -t 144000 &
You can't do this from user mode, because the power manager prevents it, so you'll need to use a kernel extension, such as InsomniaX.
Newer version(s) of OSX make it more possible to disable certain options according to https://apple.stackexchange.com/questions/35256/mac-os-x-lion-server-how-to-disable-sleep-restart-shutdown-options-for-users

How do I shutdown, restart, or log off Windows via a bat file?

I've been using Remote Desktop Connection to get into a workstation. But in this environment, I cannot use the power options in Start Menu. I need an alternative way to shutdown or restart.
How do I control my computer's power state through the command line?
The most common ways to use the shutdown command are:
shutdown -s — Shuts down.
shutdown -r — Restarts.
shutdown -l — Logs off.
shutdown -h — Hibernates.
Note: There is a common pitfall wherein users think -h means "help" (which it does for every other command-line program... except shutdown.exe, where it means "hibernate"). They then run shutdown -h and accidentally turn off their computers. Watch out for that.
shutdown -i — "Interactive mode". Instead of performing an action, it displays a GUI dialog.
shutdown -a — Aborts a previous shutdown command.
The commands above can be combined with these additional options:
-f — Forces programs to exit. Prevents the shutdown process from getting stuck.
-t <seconds> — Sets the time until shutdown. Use -t 0 to shutdown immediately.
-c <message> — Adds a shutdown message. The message will end up in the Event Log.
-y — Forces a "yes" answer to all shutdown queries.
Note: This option is not documented in any official documentation. It was discovered by these StackOverflow users.
I want to make sure some other really good answers are also mentioned along with this one. Here they are in no particular order.
The -f option from JosephStyons
Using rundll32 from VonC
The Run box from Dean
Remote shutdown from Kip
If you are on a remote machine, you may also want to add the -f option to force the reboot. Otherwise your session may close and a stubborn app can hang the system.
I use this whenever I want to force an immediate reboot:
shutdown -t 0 -r -f
For a more friendly "give them some time" option, you can use this:
shutdown -t 30 -r
As you can see in the comments, the -f is implied by the timeout.
Brutus 2006 is a utility that provides a GUI for these options.
No one has mentioned -m option for remote shutdown:
shutdown -r -f -m \\machinename
Also:
The -r parameter causes a reboot (which is usually what you want on a remote machine, since physically starting it might be difficult).
The -f parameter option forces the reboot.
You must have appropriate privileges to shut down the remote machine, of course.
Original answer: Oct. 2008
You also got all the "rundll32.exe shell32.dll" serie:
(see update below)
rundll32.exe user.exe,**ExitWindows** [Fast Shutdown of Windows]
rundll32.exe user.exe,**ExitWindowsExec** [Restart Windows]
rundll32.exe shell32.dll,SHExitWindowsEx n
where n stands for:
0 - LOGOFF
1 - SHUTDOWN
2 - REBOOT
4 - FORCE
8 - POWEROFF
(can be combined -> 6 = 2+4 FORCE REBOOT)
Update April 2015 (6+ years later):
1800 INFORMATION kindly points out in the comments:
Don't use rundll32.exe for this purpose. It expects that the function you passed on the command line has a very specific method signature - it doesn't match the method signature of ExitWindows.
Raymond CHEN wrote:
in 2004 "What can go wrong when you mismatch the calling convention?":
The function signature required for functions called by rundll32.exe is:
void CALLBACK ExitWindowsEx(HWND hwnd, HINSTANCE hinst,
LPSTR pszCmdLine, int nCmdShow);
That hasn't stopped people from using rundll32 to call random functions that weren't designed to be called by rundll32, like user32 LockWorkStation or user32 ExitWindowsEx.
(oops)
The actual function signature for ExitWindowsEx is:
BOOL WINAPI ExitWindowsEx(UINT uFlags, DWORD dwReserved);
in 2011: "Throwing garbage on the sidewalk: The sad history of the rundll32 program"
And to make it crystal-clear:
in 2013 "What's the guidance on when to use rundll32? Easy: Don't use it":
Rundll32 is a leftover from Windows 95, and it has been deprecated since at least Windows Vista because it violates a lot of modern engineering guidelines.
Another small tip: when going the batch file route, I like to be able to abort it in case I run it accidentally. So the batch file invokes the shutdown but leaves you at the command prompt afterwards.
#echo off
echo Shutting down in 10 seconds. Please type "shutdown /a" to abort.
cmd.exe /K shutdown /f /t 10 /r
Plus, since it's on a timer, you get about the same thrill as you do when hunting in The Oregon Trail.
When remoted into a machine (target is Windows XP anyway; I am not sure about target Windows Vista), although Shutdown on the start menu is replaced by Disconnect Session or something like that, there should be one called 'Windows Security' which also does the same thing as Ctrl + Alt + End as pointed to by Owen.
You're probably aware of this, but just in case: it's much easier to just type shutdown -r (or whatever command you like) into the "Run" box and hit enter.
Saves leaving batch files lying around everywhere.
I'm late to the party, but did not see this answer yet.
When you don't want to use a batch file or type the command. You can just set focus to the desktop and then use Alt + F4.
Windows will ask you what you want to do, select shutdown or restart.
For screenshots and even a video, see: https://tinkertry.com/how-to-shutdown-or-restart-windows-over-rdp
I would write this in Notepad or WordPad for a basic logoff command:
#echo off
shutdown -l
This is basically the same as clicking start and logoff manually, but it is just slightly faster if you have the batch file ready.
Some additions to the shutdown and rundll32.exe shell32.dll,SHExitWindowsEx n commands.
LOGOFF - allows you to logoff user by sessionid or session name
PSShutdown - requires a download from windows sysinternals.
bootim.exe - windows 10/8 shutdown iu
change/chglogon - prevents new users to login or take another session
NET SESSION /DELETE - ends a session for user
wusa /forcerestart /quiet - windows update manager but also can restart the machine
tsdiscon - disconnects you
rdpinit - logs you out , though I cant find any documentation at the moment

Resources