home | list info | list archive | date index | thread index

[OCLUG-Tech] fun with xsnow

Xsnow is a fun little X app that displays falling on the root window. On
occasion Santa flies by. I decided to run xsnow on my worstation for the
amusement of my office mates while I am away on vacation. The scripts I
used are included below. If you see any errors please let me know.

goals:
	run xsnow on xdm login screen with randomized parameters
	stop xsnow and blank screen after most people have left the office
	restart xsnow with new parameters before anyone arrives in the morning

The script xsnow.sh is started in the background by Xsetup (man xdm).
The script starts xsnow, forces the monitor on, and then sleeps until
6pm. On wakeup the script turns the monitor off, kills xsnow, and sleeps
until 5:30am. Then the script repeats. At each step the script looks for
a pid/lockfile. If the file is not found the script exits. On user login
Xstartup (man xdm) attempts to kill xsnow, and the pid/lockfile is
removed.

A number of xsnow options are set with random numbers. The bash arithmetic
evaluation $(( )) is used with the built-in parameter RANDOM and the
modulus operator to generate the random numbers. The following generates
a number between 0 and 3:
	$(( RANDOM % 4 ))

The script sets some xsnow options 25% of the time. A small function
named chance was defined to return true 1 in 4 times.

To start xsnow.sh the following was added to Xsetup (/etc/X11/xdm/Xsetup on Debian):
---------- Xsetup ----------

touch /var/run/xsnow${DISPLAY}.pid
/root/xsnow.sh /var/run/xsnow${DISPLAY}.pid &

---------- Xsetup ----------

To kill xsnow and delete the pid/lockfile the following was added to
Xstartup (/etc/X11/xdm/Xstartup on Debian):
---------- Xstartup ----------

mv /var/run/xsnow${DISPLAY}.pid /var/run/xsnow${DISPLAY}.pid-delete
XSNOW_PID=$( cat /var/run/xsnow${DISPLAY}.pid-delete )
rm /var/run/xsnow${DISPLAY}.pid-delete
if ( ps ho cmd p "$XSNOW_PID" | grep \^/usr/bin/xsnow ) >/dev/null; then

---------- Xstartup ----------

This script, xsnow.sh, does most of the work.
---------- xsnow.sh ----------
#!/bin/bash

set -e 

if [ -z "$1" -a -f "$1" ]; then echo "usage: $0 XSNOW_PIDFILE"; exit 1; fi

XSNOW_PIDFILE=$1

XSNOW_PID=""

OFF_TIME="18:00"
ON_TIME="5:30"

function checklock () {
   if [ ! -f "$XSNOW_PIDFILE" ]; then exit 0; fi
}

function chance () {
   [ $(( RANDOM % 4 )) -eq 0 ];
}

function xsnow_on () {

   Xsnow="-bg SkyBlue4 -solidbg -sc snow -delay 33"
   Xsnow="$Xsnow -xspeed 3 -yspeed 4 -whirl 3"
   Xsnow="$Xsnow -ssnowdepth $(( 200 + RANDOM % 400 ))"
   Xsnow="$Xsnow -wsnowdepth $(( 100 + RANDOM % 200 ))"
   Xsnow="$Xsnow -santaspeed $(( 1 + RANDOM % 10 ))"

   if chance ; then
      Xsnow="$Xsnow -snowflakes $(( 500 + RANDOM % 500 ))"
   else
      Xsnow="$Xsnow -snowflakes $(( 75 + RANDOM % 100 ))"
   fi

   if ! chance ; then
      Xsnow="$Xsnow -notrees"
   fi   

   if chance ; then
      Xsnow="$Xsnow -nosanta"
   fi   
   
   if chance ; then
      Xsnow="$Xsnow -norudolf"
   fi   

   if chance ; then
      Xsnow="$Xsnow -nowind"
   else
      Xsnow="$Xsnow -windtimer $(( 20 + $RANDOM % 30 * 10 ))"
   fi   

   /usr/bin/xsnow $Xsnow >/dev/null &
   XSNOW_PID=$!
   
   checklock
   echo "$XSNOW_PID" > "$XSNOW_PIDFILE"
   
   sleep 10

   /usr/bin/X11/xset -dpms 
   /usr/bin/X11/xset dpms force on

}  # xsnow_on ()

function xsnow_off () {

   /usr/bin/X11/xset dpms 1200 1800 2400
   /usr/bin/X11/xset dpms force off

   if ( ps ho cmd p "$XSNOW_PID" | grep \^/usr/bin/xsnow ) >/dev/null; then
      kill "$XSNOW_PID"
   fi

}

while checklock; do

    xsnow_on
    
    sleep $(( $( date +%s -d "$OFF_TIME" ) - $( date +%s ) ))

    checklock
    
    xsnow_off

    sleep $(( $( date +%s -d "$ON_TIME" ) - $( date +%s ) ))

done


# this script is released to the public domain.
---------- xsnow.sh ----------


NOTES:
Xsnow is run as root in order to access the xauth file. I didn't see an
easy way around this. 

The workstation has a LCD screen so burnin should not be an issue. DPMS
is used to force the monitor off at night to save some power. Cpudyn
slows down the cpu. 

These scripts should be safe to run on multi display machines or
machines serving xterminals, but I have not tested anything. If I were
to run xsnow in an environment like that I would remove the dpms force
on, and force off calls. I would also leave xsnow running and just
restart it once a day in the morning.

There is a race condition. If a user logs in just as xsnow is restarted,
xdm may not kill xsnow. There are some amaturish checks to limit this
risk. This won't be an issue for my workstation. Other users may want to
make their script more robust.

The creation of the pid/lockfile may not be the best.

The utilities date, ps, and grep are used in a gnu specific way.

The xsnow.sh script is written in bash and may use bashisms. Deal with it. :-)


--
sg