-
-
Save ttys3/fb7124494e3116a4ac054cfc218bd80d to your computer and use it in GitHub Desktop.
Prevent the screen from blanking under GNOME+Wayland while a video is playing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Prevent the screen from blanking while a video is playing. | |
This script is a workaround for the GNOME+Wayland issue documented in the | |
[Disabling Screensaver] section of the mpv manual, and depends on | |
gnome-session-inhibit (usually provided by the gnome-session package) to set up | |
the inhibitors. | |
# CAVEATS | |
This is not (yet?) a foolproof solution. | |
At this time GNOME's implementation of the Inhibit protocol does not support the | |
SimluateUserActivity method: | |
dbus-send --print-reply --session \ | |
--dest=org.gnome.ScreenSaver --type=method_call \ | |
/org/gnome/ScreenSaver org.gnome.ScreenSaver.SimulateUserActivity | |
So there seems to be no way to "poke" the system with a heartbeat to extend to | |
idle timeout for a bit and prevent blanking. | |
This means that inhibitors have to be installed, then removed when mpv exits. | |
The current implementation handles this via another application, which should | |
alwasy be available on GNOME+Wayland desktops (because provided by gnome-session | |
itself): gnome-session-inhibit. | |
Executing gnome-session-inhibit to handle this is not ideal because if mpv does | |
not exit cleanly, gnome-session-inhibit is not necessarily killed (it can get | |
ophaned, and gets adopted by the init process), leaving the inhibitors intact | |
with no easy way for the user to figure it out. | |
Ideally this script should open a DBus session directly to handle the | |
inhibitors, as they would be removed if the DBus session gets disconnected (cf. | |
[Inhibit's documentation][Inhibit]): | |
> Applications should invoke [Inhibit ()] when they begin an operation that | |
> should not be interrupted [...] When the application completes the operation | |
> it should call Uninhibit() or disconnect from the session bus. | |
This is how [other applications] do it. | |
But really, the only secure way to handle this would be with a heartbeat to | |
regularly reset the idle timer, i.e. what SimulateUserActivity is supposed to | |
provide. Especially for a core security feature such as screen locking (cf. | |
xscreensaver's author [rant][jwz]). | |
# TODO | |
- Do not inhibit for audio-only playback, only for video | |
- Fix inhibitors not removed when mpv does not terminate gracefully, e.g. with | |
`kill -9 <pid>`, either by having a wrapper around gnome-session-inhibit | |
checking the parent PID (see https://stackoverflow.com/a/2035683), or by | |
interfacing with dbus to open a session that gets disconnected if mpv | |
terminates uncleanly (hopefully), or ... | |
- Investigate whether the 'playback-abort' event should be handled | |
- Only inhibit when the player is visible | |
[Disabling Screensaver]: https://mpv.io/manual/master/#disabling-screensaver | |
[Inhibit]: https://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Inhibit | |
[jwz]: https://www.jwz.org/blog/2021/01/i-told-you-so-2021-edition/ | |
[other applications]: https://unix.stackexchange.com/a/438335 | |
]] | |
local mp = require 'mp' | |
local msg = require 'mp.msg' | |
local cookie | |
local function handle_inhibit(_, paused) | |
if paused and cookie then | |
mp.abort_async_command(cookie) | |
cookie = nil | |
msg.debug('inhibit off') | |
elseif not paused and not cookie then | |
cookie = mp.command_native_async( | |
{ | |
name = 'subprocess', | |
args = { | |
'gnome-session-inhibit', | |
'--inhibit-only', | |
'--inhibit', 'idle', | |
'--app-id', 'mpv', | |
'--reason', 'video-playing', | |
}, | |
-- `playback_only = true` does not kill the command when | |
-- playback is paused (i.e. "pause" is still "playback == true") | |
-- but also kills it immediately the first time. | |
-- So we need to handle the paused state ourselves. | |
playback_only = false, | |
-- If not captured, mpv will just forward everything to stdout | |
-- and stderr. Setting capture_stdXXX with capture_size = 0 will | |
-- ensure that the command's output is discarded. | |
capture_stdout = true, | |
capture_stderr = true, | |
capture_size = 0, | |
}, | |
-- Should be optional according to the doc, but I get an error if I | |
-- don't provide a function: attempt to call local 'cb' (a nil value) | |
function() end | |
) | |
msg.debug('inhibit on') | |
end | |
end | |
mp.observe_property('stop-screensaver', 'bool', function(_, enable) | |
if enable then | |
mp.observe_property('pause', 'bool', handle_inhibit) | |
msg.debug('inhibit handling on') | |
else | |
mp.unobserve_property('pause', 'bool', handle_inhibit) | |
msg.debug('inhibit handling off') | |
if cookie then | |
mp.abort_async_command(cookie) | |
cookie = nil | |
msg.debug('inhibit off') | |
end | |
end | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment