Commit c1f6c854 authored by Jarno Suni's avatar Jarno Suni
Browse files

xflock4: Improve support for lockers and DPMS (#17,#29)

Add some shell settings to make errors more noticeable and to improve

Run the shell command read from Xfconf property /general/LockCommand
in separate shell, if the property exists and is non-null. The property
can be a complex shell one-liner that is encapsulated within xflock4.
If the one-liner fails, xflock4 fail, too.

Support new xfconf properties of xfce4-session channel:

/general/LockCommandForks (boolean; default false):

If LockCommand does not fork, this has to be false to get xflock4 exit
before unlocking; it will exit not later than one second after
launching the command then. If LockCommand exits by that time with
non-zero exit status then xflock4 fails; otherwise it is regarded as

/general/LockSetDPMS (boolean, default is false):

If true, change DPMS setting for the locker. The setting does not apply
to fallback screensavers, though.

/general/LockInitialDelayDPMS (unsigned integer; default is 1):

Apply DPMS mode after this initial delay in seconds after the locker is
started. Actual delay is at least 1 second if /general/LockCommandForks
is false. (Closes #29)

/general/LockDelayDPMS (unsigned integer; default is 15):

Apply DPMS mode after this amount of time of inactivity in seconds.
The old setting is restored after locker exits. This does not have an
effect if /general/LockCommandForks is true. This does not have an
effect either if the locker set in /general/LockCommand forks (because
it exits right off).

/general/LockModeDPMS (string with allowed values
standby, suspend and off; default is off):

Some DPMS mode might work better than other depending on system.

Recognize new screensaver:
light-locker (which also turns audio off while locking)

xscreensaver-command can take GNU style options so it can be called
from the loop.

'gnome-screensaver-command --lock' returns always 0, so it has to be
handled specially. Tested on gnome-screensaver 3.6.1; the project
seems to be unmaintained.

Remove hard-coded fallback lockers because user can specify which
locker to use by LockCommand property.

Update copyright.
parent 9250db9f
......@@ -2,9 +2,8 @@
# xfce4
# Copyright (C) 1999, 2003 Olivier Fourdan (
# Copyright (C) 2011 Guido Berhoerster (
# Copyright (C) 2011 Jarno Suni (
# Copyright (C) 2015, 2018 Simon Steinbeiß (
# Copyright (C) 2014..2021 Jarno Suni (
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -20,35 +19,136 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
set -eu
unset -v IFS
# First test for the command set in the session's xfconf channel
LOCK_CMD=$(xfconf-query -c xfce4-session -p /general/LockCommand)
# Sleep time given in decimal number $1. The unit is second.
# Try to do that even in a system in which sleep command does not
# support non-integer time.
real_sleep() {
# The default sleep might not accept non-integer argument
sleep "$1" || perl -e "select(undef, undef, undef, $1)" \
|| sleep "$(( ${1%%.*} + 1 ))" # in case perl is not installed
# and non-integer sleep is supported, determine time by ceiling
# function to get at least given amount of delay.
} 2>/dev/null
# Lock by xscreensaver or gnome-screensaver, if a respective daemon is running
for lock_cmd in \
"xfce4-screensaver-command --lock" \
"xscreensaver-command -lock" \
"gnome-screensaver-command --lock"
if [ ! -z "$lock_cmd" ]; then
$lock_cmd >/dev/null 2>&1 && exit
# Turn off display after given time in seconds.
initial_dpms() {
sleep "$1"
xset dpms force $DPMS_MODE
# Run command with DPMS, if wanted.
# First argument is boolean one telling, if this should touch DPMS setting.
# The second argument is the shell command string to run.
command_dpms() {
[ "$set_dpms" = true ] && {
revert() {
xset dpms ${xstate}dpms
# Handle POSIX signals whose default action is to terminate
# (except POLL because it does not work with dash)
# Save the state of DPMS; rely on the output format of `xset q`.
xstate=$(xset q | awk '/^DPMS /{getline;
printf "%s %s %s ", $2, $4, $6; getline;
if($3=="Enabled"){print "+"}else{print "-"}exit}')
# Enable monitor power management
xset dpms $dpms_string
/bin/sh -c "$2" || error_code=$?
[ "$set_dpms" = true ] && revert # Restore DPMS state
return $error_code
default() {
printf 'Using default value %s.\n' "$1" >&2
printf %s "$1"
# get unsigned integer value
DPMS_INITIAL_DELAY=$(xfconf-query -c xfce4-session \
-p /general/LockInitialDelayDPMS || default 1)
# get unsigned integer value
DPMS_DELAY=$(xfconf-query -c xfce4-session \
-p /general/LockDelayDPMS || default 15)
# get string
DPMS_MODE=$(xfconf-query -c xfce4-session -p /general/LockModeDPMS \
|| default off)
case "$DPMS_MODE" in
standby) dpms_string="$DPMS_DELAY 0 0";;
suspend) dpms_string="0 $DPMS_DELAY 0";;
off) dpms_string="0 0 $DPMS_DELAY";;
*) printf 'Invalid DPMS mode: %s
Allowed values are standby, suspend and off\n' "$DPMS_MODE" >&2; exit 1
# Use a custom lock command, if given.
LOCK_CMD=$(xfconf-query -c xfce4-session -p /general/LockCommand || :)
[ -n "$LOCK_CMD" ] && {
# Get boolean value to decide whether or not to apply display
# power management :
LOCK_SET_DPMS=$(xfconf-query -c xfce4-session \
-p /general/LockSetDPMS || default false)
# Get another boolean value to tell, if the LOCK_CMD forks or not.
# If it forks (i.e. the value is "true") this script can not apply
# display power management and the setting above is ignored except
# for turning display off initially.
LOCK_FORKS=$(xfconf-query -c xfce4-session \
-p /general/LockCommandForks || default false)
[ "$LOCK_FORKS" = true ] && {
command_dpms false "$LOCK_CMD" || exit_code=$?
} || {
command_dpms "$LOCK_SET_DPMS" "$LOCK_CMD" &
sleep 1 & # wait at most a second
# Test, if command_dpms is running after small delay.
while kill -0 "$pid" 2>/dev/null; do
kill -0 "$pid_sleep" 2>/dev/null || {
# command_dpms still running after the maximum delay.
# Suppose LOCK_CMD locked.
[ "$LOCK_SET_DPMS" = true ] \
&& initial_dpms $((DPMS_INITIAL_DELAY > 1 \
? DPMS_INITIAL_DELAY - 1 : 0)) &
exit 0
real_sleep 0.1 # Take a breath
# command_dpms finished; get the exit code
exit_code=0; wait "$pid" || exit_code=$?
[ "$exit_code" -eq 0 ] && {
# Suppose LOCK_CMD forked and screen is locked.
[ "$LOCK_SET_DPMS" = true ] \
&& initial_dpms $DPMS_INITIAL_DELAY &
exit 0
>&2 printf "'%s' exited with error %s\n" "$LOCK_CMD" "$exit_code"
exit 1
# else run another access locking utility, if installed
for lock_cmd in \
"xlock -mode blank" \
set -- $lock_cmd
if command -v -- $1 >/dev/null 2>&1; then
$lock_cmd >/dev/null 2>&1 &
# turn off display backlight:
xset dpms force off
xfce4-screensaver \
xscreensaver \
# To redirect stdin to /dev/null is needed at least for xscreensaver
# to not have 1 s delay when invoked on terminal.
${lock_cmd}-command --lock </dev/null 2>/dev/null && exit
# else access locking failed
exit 1
# 'gnome-screensaver-command --lock' always returns 0 even if it does
# not lock. (Version 3.6.1 at least.)
# Thus running with '--query' first is needed.
[ -n "$(gnome-screensaver-command --query 2>/dev/null)" ] \
&& gnome-screensaver-command --lock </dev/null 2>/dev/null
......@@ -4,6 +4,11 @@
<property name="general" type="empty">
<property name="FailsafeSessionName" type="string" value="Failsafe"/>
<property name="LockCommand" type="string" value=""/>
<property name="LockCommandForks" type="bool" value="false"/>
<property name="LockSetDPMS" type="bool" value="false"/>
<property name="LockModeDPMS" type="string" value="off"/>
<property name="LockInitialDelayDPMS" type="uint" value="1"/>
<property name="LockDelayDPMS" type="uint" value="15"/>
<property name="sessions" type="empty">
<property name="Failsafe" type="empty">
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment