# HOWTO: Deactivate xscreensaver while watching online videos

## VinzC

Hi all. This is a sequel from a thread I posted a while ago. I came out with this script today and I have just tested it while watching videos on Youtube with Firefox. The script can be updated for use with any browser though.

See also Syl20 nice & simple implementation.

The background story

I love xscreensaver. But I just hate it when I have to tuck it off while I'm away from the keyboard, watching videos, for instance. On one hand the developers of mplayer have come with a nifty solution: heartbeat-cmd. Unfortunately you can't expect Mozilla to implement the same trick. So we have to do it ourselves, right?...

Prerequisite

As krinn rightfully hints, /proc/pid/io may not always be available. You have to make sure your kernel configuration includes the following:

```
Enable extended accounting over taskstats (CONFIG_TASK_XACCT)

* Enable per-task storage I/O accounting (CONFIG_TASK_IO_ACCOUNTING=y)
```

Note that very same configuration is required if you use iotop.

How does it work

It's best (IMHO) to have the script run on as many distributions as possible instead of relying on extensions or modules, which are browser- or desktop-environment-dependent. Xorg comes with plenty of tools, which used appropriately can get us close to what we want.

Instead of deactivating xscreensaver from within Firefox, we want to a) detect what the active (i.e. currently focused) window is, b) check the process it belongs to, c) if it is busy downloading (a video, whatever...) While a) and b) are easy, c) is less straightforward. How can we detect a browser that pulls down a video from Youtube? Or any video? Check the URL? No, too cumbersome.

A browser that shows a video is supposed to cause disk activity. In general (and it's true with Youtube) videos aren't downloaded at once (unless they're very short) but slowly enough to not hog up the whole bandwidth. That's the most daring assumption I made. If you check the kernel I/O information for a given process ID you'll see a few hints as to what amount of data a process is pulling off or throwing at the storage. Check write_bytes in /proc/<PID>/io for short.

If the browser doesn't write to the disk for 30 seconds the script exits. Otherwise it sends the deactivate beacon to xscreensaver.

EDIT: Here's an updated version that I run on my laptop. The update accounts for the presence of a power manager, which may co-exist with a screen saver such as xscreensaver. In such cases deactivate screen saving is just not enough. The trick I used is to switch to presentation mode (an underused featured, speaking for myself) to temporarily disable energy saving while the screen is busy but no input is detected. Since complexity slightly increased, I also included a self-explained help section, you know, just in case  :Wink: .

Running it from the command line is also improved, along with rudimentary diagnostics. I also introduced a way to support other DE than just Xfce. I didn't implement it though but I thought of dynamically loading DE modules (see xfce_ prefixes. It would require a dedicated command line argument but it should be not too difficult to add.

One last note: it might interfere with the presentation settings on your machine. I don't use it at all so I didn't think of a non-intrusive solution. Feel free to comment & suggest if you use that feature with your power manager application  :Cool:  .

Important note: I also changed the command line interface, which now requires a -P option for gathering the DISPLAY variable.

Here's the script.

```
#!/bin/sh

#

# This script tries to inhibit screen saving while there's a browser

# which is detected active, i.e. exhibiting disk activity for a given

# amount of time. The detection algorithm is triggered only when the

# browser window is on the foreground, i.e. the window with the focus.

#

# The script may be run from the command line or a cron job. The main

# goal is to compensate for the lack of features in browsers to block

# or suspend power savings while the screen is busy, say rendering

# videos, for example.

#

# Author: Vinz C. <vinzc@users.sf.net>

# Configuration variables. These affect how long a program remains idle

# i.e. without disk activity. Disk activity is checked as many times as

# MAX_COUNT (in seconds) for a total amount of LOOP_DELAY x MAX_COUNT .

#

MAX_COUNT=3      # Number of times no disk activity is detected

LOOP_DELAY=10      # Seconds to wait once activity is detected

VERBOSE=false

# [Supposedly] read-only variable

NAME=${0##*/}      # This script's name

# Process watch indicators

last_write_bytes=0   # Most recent bytes written to disk

count=0         # Iteration count

# Writes a message to the standard error or the system log

# on a non interactive shell

message()

{

   tty -s && echo "$*">&2 || logger -t $NAME "$*"

}

# Writes an error message to the standard error or the system log

# on a non interactive shell and exit with a return code if non zero

error()

{

   local ret=$1

   shift && message "$*"

   [ $ret -eq 0 ] || exit $ret

}

# Writes a message to the standard error or the system log

# on a non interactive shell but only in verbose mode

verbose()

{

   $VERBOSE && message "$*"

}

# Find the DISPLAY environment variable of a given process

find_display()

{

   local PID=$(pgrep -u $UID $1) || return $?

   eval $(grep -Eoa 'DISPLAY=[:09.]+' /proc/$PID/environ)

}

# Return non-zero if the given process window has he focus on DISPLAY

#

# Force xprop formatting to output eval assignment expressions, such

# as '_NET_ACTIVE_WINDOW' and '_NET_WM_PID' (eval doesn't accept spaces

# around the equal sign.)

pid_if_active()

{

   # Get active window ID

   local _NET_ACTIVE_WINDOW

   eval $(xprop -root -notype -f _NET_ACTIVE_WINDOW 32x '=$0\n' _NET_ACTIVE_WINDOW 2>/dev/null)

   [ -n "$_NET_ACTIVE_WINDOW" ] || return 1

        # Get its process ID, set PID if found

   local _NET_WM_PID

   eval $(xprop -id $_NET_ACTIVE_WINDOW -notype -f _NET_WM_PID 32c '=$0\n' _NET_WM_PID 2>/dev/null)

   [ -n "$_NET_WM_PID" ] || return 1

        # Is it the process which name was passed as an argument?

        grep -Eqs "$@" /proc/$_NET_WM_PID/cmdline && PID=$_NET_WM_PID

}

# Deactivate (temporarily) the screensaver for DISPLAY

keep_active()

{

   # Keep useless babling out...

   DISPLAY=$DISPLAY xscreensaver-command -deactivate >/dev/null

}

# Xfce-specific: enable/disable presentation mode

xfce_presentation_mode()

{

   $XFCES_POWER_MGR && [ -n "$1" ] && xfconf-query -c xfce4-power-manager \

      -p /xfce4-power-manager/presentation-mode -s $1

}

# Xfce-specific: deactivate screen saver and set presentation mode

# On Xfce presentation mode doesn't (seem to) prevent xsvreensaver

# from activating itself so both must be taken care of.

xfce_keep_active()

{

   keep_active

   xfce_presentation_mode true

}

usage()

{

   cat <<-EOF

   Usage: $NAME [-h][-P name][-d delay][-c count][-v][--debug] process

   Watch for disk activity of a process and temporarily disable power saving

   until no disk activity is detected. This script is intended for use when

   watching videos in a[ny] browser since none of them include features to

   disable power saving while the screen is busy and input is idle.

   process      Process (typically a browser name (not PID) to watch for.

   -P name      Fetch the DISPLAY environment variable from process name.

          If run from Xfce "name" will be xfce4-session. This is

          useful when running multiple DE or on a machine with more

          than one user account.

          The DISPLAY environment variable is deduced and passed to

          \`xprop´ and other applications that need it to work.

   -d delay   Delay (in seconds) to wait between loops once disk activity

          is detected.

   -c count   Loop no more than \`count´ if there's no disk activity.

   -v      Verbose mode (e.g. when run from the command line)

   --debug      Run the script with \`set -x´

   -h      This help screen.

   DESCRIPTION

   This script may be run as a cron job, in whch case -P is required for a

   valid DISPLAY variable to exist for monitoring X properties. It can also

   be run from the command line, in a terminal or from a TTY. In the latter

   case the argumen -P is also required and for the same reasons.

   The algorithm used in this script assumes every browser caches what it

   fetches from the interwebs, which should include video streams as well.

   So it might fail in very predictible circumstances and specific contexts.

   The script first detects if the process, which name is passed as an argument

   has its window in the foreground then monitors disk activity. Power saving

   is temporarily disabled and presentation mode enabled (if present) as soon

   as disk activity is detected. If no disk activity is detected then power

   saving is "restored" — i.e. set to default values because the script is

   stateless.

   USE CASES

   When run as a cron job the period should be less than the idle delay that

   triggers power (or screen) saving. For example, on a laptop where screen

   blanking occurs after 20 minues and xscreensaver is run after 10 minutes,

   the script might need to run every five minutes.

   EOF

}

# Parse command line arguments

while getopts "hP:vc:d:-:" opt; do

   case $opt in

   P)   find_display $OPTARG || error $? "No DISPLAY found for $OPTARG." ;;

   d)   LOOP_DELAY=$OPTARG ;;

   c)   MAX_COUNT=$OPTARG ;;

   v)   VERBOSE=true ;;

   h)   usage && exit 0 ;;

   -)   case "$OPTARG" in

      debug)   set -x ;;

      esac ;;

   ?)   usage && exit 1 ;;

   esac

done && shift $((OPTIND - 1)) || exit $?

# Don't run twice on the same process

pgrep -u $UID -f "$NAME" | grep -v $$ | xargs ps | grep -qE "$0\s+.*$1" && \

   message "Already watching ..." && \

   exit 0

# Abort if there's no detected DISPLAY

[ -n "$DISPLAY" ] || error $? "No display found."

export DISPLAY

pid_if_active $1 || exit 0

verbose "Watching $PID ..."

# Xfce-specific: Detect the presence of a (running) power manager

XFCE_POWER_MGR=false

pgrep -u $UID -f 'xfce4-power-manager' >/dev/null && XFCE_POWER_MGR=true

$XFCE_POWER_MGR && verbose "Using Xfce power manager"

# Set/reset presentation settings on exit (disabled by default

# except when disk activity is detected while the process has

# the focus)

$XFCE_POWER_MGR && trap "xfce_presentation_mode false" EXIT INT

while [ $count -lt $MAX_COUNT ]; do

   # Check active window process. If the window is not active,

   # power saving will be restored

   pid_if_active $1 || exit 0

   # Check how many bytes the process has written to disk.

   # The loop will track the process for as long as it detects

   # some disk activity...

   eval $(sed -rn '/^write_bytes/s/:\s+/=/p' /proc/$PID/io || echo write_bytes=0)

   if [ $last_write_bytes -ne $write_bytes ]; then

      if [ $last_write_bytes -ne 0 ]; then

         xfce_keep_active

         verbose "Detected $((write_bytes - last_write_bytes)) bytes written"

      fi

      last_write_bytes=$write_bytes

      count=0

   else

      (( count++ ))

      verbose "$NAME: No activity detected ($count)"

   fi

   sleep ${LOOP_DELAY}

done

# This forces a clean exit code if run from cron:

exit 0

```

EDIT: Bug fixed in the 10s loop; every time the script is executed might prevent the screensaver from running for good since last_write_bytes always starts from 0, which always differs from what's in /proc/$_NET_WM_PID/io on the first iteration.

I successfully ran it from a cron job for a couple of hours while watching videos on Youtube:

```
*/5 * * * *     /usr/local/bin/keep-active.sh -P xfce4-session firefox
```

The script runs with bash. Making it run with dash shouldn't be too difficult. On my machine the screen saver delay is set to 10 minutes. So far it has been enough. The screen saver started as expected after I closed the video tab and left my machine idle for a little more than 10 minutes. Not sure one needs to close the video tab though.

One caveat is that the screen saver will also be deactivated if you leave your browser active while downloading something. Just switch to another window to fix that.

If you want to check, say, seamonkey and firefox, thanks to the grep clause:

```
*/5 * * * *     /usr/local/bin/keep-active.sh -P xfce4-session 'seamonkey|firefox'
```

Hope this helps.

----------

## krinn

some notes:

```
 sleep 3 && xprop -root -notype -f _NET_ACTIVE_WINDOW 32x '=$0\n' _NET_ACTIVE_WINDOW

(sleep 3 so i have time to click on firefox windows)

_NET_ACTIVE_WINDOW=0x100008e

xprop -id 0x100008e -notype -f _NET_WM_PID 32c '=$0\n' _NET_WM_PID

_NET_WM_PID=5939

```

```
pidof firefox

5939
```

```
LANG=C ls /proc/5939/i*

ls: cannot access '/proc/5939/i*': No such file or directory
```

- are you sure /proc/pid/io is always there? for my firefox i have none. making the test for write_bytes not that reliable

- i fail to see the fullscreen check. my tests were made with firefox not fullscreen and they pass it well.

edit: in case it help, my "dirty" solve to that problem is: open vlc with a movie, hit pause: vlc disable screen saving (i have no screensaver to disable myself, except the monitors getting off)

----------

## VinzC

 *krinn wrote:*   

> some notes:
> 
> ...
> 
> - are you sure /proc/pid/io is always there? for my firefox i have none. making the test for write_bytes not that reliable
> ...

 

Dumb me!... I wrote this HOWTO right before going to sleep. Indeed there's no check for _NET_WM_STATE_FULLSCREEN as I later acknowledged any focused web browser window that is using storage continuously must mean to deactivate the screen saver... So the check was there in a first draft of my script, now it's gone. So indeed if you are viewing videos, the screen saver should be deactivated regardless of the full screen state. I'll update my post accordingly.

As for your second point, I can tell it's also present on Manjaro, which runs my laptop. It's filed in your kernel config "Enable per-task storage I/O accounting" (CONFIG_TASK_IO_ACCOUNTING).

 *krinn wrote:*   

> edit: in case it help, my "dirty" solve to that problem is: open vlc with a movie, hit pause: vlc disable screen saving (i have no screensaver to disable myself, except the monitors getting off)

 

I don't have VLC on my Gentoo machine and wanted as few dependencies as possible.

----------

## krinn

```
grep CONFIG_TASK /usr/src/linux/.config

CONFIG_TASKSTATS=y

CONFIG_TASK_DELAY_ACCT=y

# CONFIG_TASK_XACCT is not set

# CONFIG_TASKS_RCU is not set

```

Well at least good to note it need a kernel option, but from my kernel, it also mean some minimal kernel version. (i'm using 3.18.30)

----------

## VinzC

 *krinn wrote:*   

> 
> 
> ```
> grep CONFIG_TASK /usr/src/linux/.config
> 
> ...

 

You need to enable CONFIG_TASK_XACCT to see CONFIG_TASK_IO_ACCOUNTING. Updating the thread  :Smile:  ... Done.

----------

## Syl20

I'm looking for a solution to this problem, more or less, for years... So I made some tests yesterday, but, unfortunately, Firefox is too much quiet : the "write_bytes" field isn't increased often enough to block the screensaver on my computer.  :Crying or Very sad: 

My config : amd64 stable, firefox 46, watching Youtube HTML5 videos, xscreensaver configured to blank the screen after five minutes of inactivity, and the script is cronned every two minutes (so there are two executions before the screensaver starting).

That said, this way is interesting, thank you. I'll try to dig a little more, to see if I can find something working on my systems.

----------

## VinzC

 *Syl20 wrote:*   

> I'm looking for a solution to this problem, more or less, for years... So I made some tests yesterday, but, unfortunately, Firefox is too much quiet : the "write_bytes" field isn't increased often enough to block the screensaver on my computer. 
> 
> My config : amd64 stable, firefox 46, watching Youtube HTML5 videos, xscreensaver configured to blank the screen after five minutes of inactivity, and the script is cronned every two minutes (so there are two executions before the screensaver starting).
> 
> That said, this way is interesting, thank you. I'll try to dig a little more, to see if I can find something working on my systems.

 

There are a few ways from here. A couple of options I thought of looking into the window title with xprop monitoring firefox's network traffic — which would maybe require throttling with fast internet connections, in case the download process is too quick find another field from /proc/<pid> to monitoruse the fullscreen property alone to deactivate xscreensaver Maybe also the idle delay you gave xscreensaver is a little too short. I know some applications do restrain from writing to the disk for some period. Don't know if it's the case with firefox but you might want to increase xscreensaver idle delay up to 10 minutes like I did and, why not, increase the number of 10s loops in the script...

----------

## Syl20

I should have some time to spend on this next weekend. Thank you for the tips.

----------

## krinn

how about netstat -p | firefox

if you get an ESTABLISHED state, it is at work.

----------

## frankenputer

 *krinn wrote:*   

> how about netstat -p | firefox
> 
> if you get an ESTABLISHED state, it is at work.

 

```
netstat -n | gawk '/^tcp/ {state=$NF} END {print state}'

netstat -n | gawk \

  '/^tcp/ { 

    st[$NF]++;

  }

  END {

    for (k in st) {

      print k,"\t",st[k];

    }

  }'

```

Edit:

I think the more correct approach will be to check the CPU load and if it matches certain load to execute xset -dmps and xset s off

```
ps -eo pcpu,comm | gawk '{ if ($1 > 20) { print $1,$2 } }'

# 32.4 chrome
```

----------

## VinzC

 *krinn wrote:*   

> how about netstat -p | firefox
> 
> if you get an ESTABLISHED state, it is at work.

 

Might be worth trying  :Cool:  . I believe keep alive connections/scripts might defeat this test but definitely worth combining with other detection steps.

 *frankenputer wrote:*   

> I think the more correct approach will be to check the CPU load and if it matches certain load to execute xset -dmps and xset s off
> 
> ```
> ps -eo pcpu,comm | gawk '{ if ($1 > 20) { print $1,$2 } }'
> 
> ...

 

Hmmm... not sure. Web sites with heavy animations might defeat the detection on a "non-accelerated-graphics" (i.e. between old and very, very old) machine, for instance.

----------

## Syl20

Ok, I've a working script. I decided to deactivate the screensaver only when the browser window has both focused and fullscreen (not maximized) states. That does the job, according to my habits.

```
#!/bin/bash

# This script tries to inhibit the screen saver while Firefox (or any other

# browser) is full screen. It is intended to run as a cron job every

# minute or so, i.e. any period shorter than xscreensaver lock time.

#                                                                                                                           

# Note this script uses xprop formatting to output eval compatible assignment

# expressions, such as _NET_ACTIVE_WINDOW and _NET_WM_PID. IF we don't do

# that xprop output contains extraneous spaces that mess things up.

set -uef -o pipefail

                                                                                                                            

[ -z "$@" ] && exit 1

NAV="$1"        # Browser, as it appears in `ps` list

# First, is the browser running ?

PID=$(pgrep --delimiter " " --uid "$UID" "$NAV")

[ -z "$PID" ] && exit 0

# Second, am I focused on it ?

DISPLAYS=""                                                                                                                 

DISPLAY=""                                                                                                                  

for pid in $PID

do

        eval $(grep -Eoa 'DISPLAY=[:09.]+' "/proc/$pid/environ")

        [ -z "$DISPLAY" ] && continue

        DISPLAYS="$DISPLAYS $DISPLAY"

done                                                                                                                        

                                                                                                                            

for DISPLAY in $(echo $DISPLAYS | tr " " "\n" | sort -u)

do

        export DISPLAY

        # Get active window ID

        eval $(xprop -root -notype -f _NET_ACTIVE_WINDOW 32x '=$0\n' _NET_ACTIVE_WINDOW)

        [ -z "$_NET_ACTIVE_WINDOW" ] && exit 1

        # Get its process ID

        eval $(xprop -id "$_NET_ACTIVE_WINDOW" -notype -f _NET_WM_PID 32c '=$0\n' _NET_WM_PID)

        [ -z "$_NET_WM_PID" ] && exit 1

        # Check the process name

        grep -q "$NAV" "/proc/$_NET_WM_PID/cmdline" || continue

        # Third, is the browser window full-screen ?

        xprop -id "$_NET_ACTIVE_WINDOW" -notype "_NET_WM_STATE" | grep -q "_NET_WM_STATE_FULLSCREEN" || continue

        # Ok, all the requirements are met.

        xscreensaver-command -deactivate > /dev/null 2>&1

        xset dpms s reset > /dev/null 2>&1

        exit 0

done 
```

Even if firefox is still mono-processus, I decided to make this script multi-processus compliant. But, most of the times, on a personal computer, there's only one display used at a given time. Rarely two. Splitting the windows properties detections into two different for loops limits the number of executed commands.

I had to add the "xset dpms s reset" command to deactivate all the screen blanking capabilities.

Thank you VinzC for the ideas, the base script, and the motivation to finish this long quest...  :Laughing: 

----------

## VinzC

 *Syl20 wrote:*   

> Thank you VinzC for the ideas, the base script, and the motivation to finish this long quest... 

 

My pleasure. Glad you found a working solution   :Cool:  .

----------

## VinzC

I updated my script to work on laptops with a power management application such as xfce4-power-manager. I was annoyed my script didn't prevent the screen from blanking in some circumstances so I decided to improve it. Here's the results.

Peace, Merry Christmas and Happy New Year everyone.

----------

