http://ftp.x.org/contrib/applications/xscreensaver-3.18.tar.gz
[xscreensaver] / hacks / vidwhacker
1 #!/bin/sh
2 #
3 # vidwhacker, for xscreensaver.  Copyright (c) 1998, 1999 Jamie Zawinski.
4 #
5 # Permission to use, copy, modify, distribute, and sell this software and its
6 # documentation for any purpose is hereby granted without fee, provided that
7 # the above copyright notice appear in all copies and that both that
8 # copyright notice and this permission notice appear in supporting
9 # documentation.  No representations are made about the suitability of this
10 # software for any purpose.  It is provided "as is" without express or 
11 # implied warranty.
12 #
13 #
14 # This script grabs a frame of video, then uses various pbm filters to
15 # munge the image in random nefarious ways, then uses xli or xv to put it
16 # on the root window.  This works out really nicely if you just feed some
17 # random TV station into it...
18 #
19 # The video grabbing part is SGI-specific -- if you want to use this on
20 # another system, add a new clause to the grab() procedure.
21
22
23 # need perl to generate random numbers -- I don't know another way to do
24 # that from a shell script.
25 perl=perl
26
27
28 onroot=false
29 verbose=false
30 delay=3
31 use_stdin=false
32 use_stdout=false
33
34 pid=""
35 tmp=${TMPDIR:-/tmp}/vidwhacker.$$
36 tmp_rgb=$tmp-00000.rgb
37 tmp_ppm0=$tmp-0.ppm
38 tmp_ppm1=$tmp-1.ppm
39 tmp_ppm2=$tmp-2.ppm
40 tmp_ppm3=$tmp-3.ppm
41 tmp_ppm4=$tmp-4.ppm
42 tmp_ppmS=$tmp-S.ppm
43
44
45 # Figure out whether to use xli or xv.
46 pick_displayer() {
47   IFS=:
48   for p in $PATH; do
49     if [ "$p" = "" ]; then p=. ; fi
50     if [ -x $p/xli ]; then
51       displayer=$p/xli
52       displayer_args="-quiet"
53       displayer_win_args=""
54       displayer_root_args="-onroot -center -border black"
55       return
56     elif [ -x $p/xv ]; then
57       displayer=$p/xv
58       displayer_args="-quick24"
59       displayer_win_args="-geom +0+0"
60       displayer_root_args="-root -rmode 5 -noresetroot -rfg black -rbg black -viewonly"
61
62       return
63     fi
64   done
65   IFS=
66
67   echo "$0: neither xli nor xv found on \$PATH"
68   exit -1
69 }
70
71 # Process command-line args
72 getargs() {
73
74   while [ $# != 0 ]; do
75     case "$1" in
76     -display | -disp | -dis | -dpy | -d )
77       shift
78       DISPLAY="$1"
79       export DISPLAY
80       ;;
81     -root )
82       onroot=true
83       ;;
84     -window )
85       onroot=false
86       ;;
87     -verbose )
88       verbose=true
89       ;;
90     -stdin )
91       use_stdin=true
92       ;;
93     -stdout )
94       use_stdout=true
95       ;;
96     -delay)
97       shift
98       delay="$1"
99       ;;
100     * )
101       echo "VidWhacker, Copyright (c) 1999 Jamie Zawinski <jwz@jwz.org>" >&2
102       echo "            http://www.jwz.org/xscreensaver/" >&2
103       echo "" >&2
104       echo "usage: $0 [-display dpy] [-verbose] [-root | -window]" >&2
105       echo "                  [-stdin] [-stdout] [-delay secs]" >&2
106       exit 1
107       ;;
108     esac
109     shift
110   done
111
112   pick_displayer
113
114   if [ "$onroot" = true ]; then
115     displayer_args="$displayer_args $displayer_root_args"
116   else
117     displayer_args="$displayer_args $displayer_win_args"
118   fi
119
120
121   screen_width=''
122   if [ "$use_stdout" = false ]; then
123     screen_width=`xdpyinfo 2>/dev/null | 
124         sed -n 's/.* dimensions: *\([0-9]*\).*/\1/p'`
125     if [ "$screen_width" = "" ]; then
126       screen_width=800
127     fi
128   fi
129 }
130
131
132 clean() {
133   rm -f $tmp_rgb $tmp_ppm1 $tmp_ppm2 $tmp_ppm3 $tmp_ppm4
134 }
135
136 clean2() {
137   clean
138   rm -f $tmp_ppm0 $tmp_ppmS
139 }
140
141
142 # Grab a frame of video.  leaves it in $tmp_ppm1.
143 #
144 grab() {
145   uname=`uname`
146   if [ $uname = IRIX ]; then
147     #
148     # SGI's "vidtomem" returns an SGI RGB image of the default video input,
149     # and has stupid non-overridable ouput-file-naming conventions.  So, let 
150     # it write its file; and then convert it to a pgm.
151     #
152     
153     vidtomem -f $tmp
154     sgitopnm $tmp_rgb > $tmp_ppm1
155
156     # Cut off the close-captioning blips in the NTSC overscan region.  YMMV.
157     #  | pnmcut 12 7 695 477 
158
159   elif [ $uname = Linux ]; then
160
161     # Marcus Herbert says the following works with his Connectix Qcam.
162     # Don't have qcam?  Well, do something else then...  and send me a patch.
163
164     qcam > $tmp_ppm1
165
166     # Friedrich Delgado Friedrichs says the following works if you have
167     # XawTV installed:
168     #
169     #   streamer -o $tmp_ppm1
170     #
171
172   else
173     echo "$0: don't know how to grab video on this OS." >&2
174     clean2
175     exit 1
176   fi
177 }
178
179
180 # Use perl to pick a random foreground/background color in pbm's syntax.
181 #
182 randcolor() {
183   $perl -e 'srand(time ^ $$);
184             printf("#%02x%02x%02x-#%02x%02x%02x",
185                    int(rand()*60),
186                    int(rand()*60),
187                    int(rand()*60),
188                    120+int(rand()*135),
189                    120+int(rand()*135),
190                    120+int(rand()*135))'
191 }
192
193 rand() {
194   $perl -e "srand(time ^ $$); print int(rand() * $1)"
195 }
196
197
198
199 # Frobnicate the image in some random way.
200 #
201 frob() {
202
203   w_h=`head -2 $tmp_ppm1 | tail -1`
204   width=`echo $w_h | awk '{print $1}'`
205   height=`echo $w_h | awk '{print $2}'`
206
207   N=`rand 17`
208
209   if [ "$verbose" = true ]; then
210     echo "mode $N..." >&2
211   fi
212
213   if   [ $N = 0 ]; then
214     ppmtopgm $tmp_ppm1 | pgmedge | pgmtoppm `randcolor` | ppmnorm
215
216   elif [ $N = 1 ]; then
217     ppmtopgm $tmp_ppm1 | 
218     pgmenhance | 
219     pgmtoppm `randcolor`
220
221   elif [ $N = 2 ]; then
222     ppmtopgm $tmp_ppm1 | pgmoil | pgmtoppm `randcolor`
223
224   elif [ $N = 3 ]; then 
225     ppmrelief $tmp_ppm1 | ppmtopgm | pgmedge | ppmrelief | ppmtopgm |
226       pgmedge | pnminvert | pgmtoppm `randcolor`
227
228   elif [ $N = 4 ]; then
229     ppmspread 71 $tmp_ppm1 > $tmp_ppm2
230     pnmarith -add $tmp_ppm1 $tmp_ppm2
231
232   elif [ $N = 5 ]; then
233     pnmflip -lr $tmp_ppm1 > $tmp_ppm2
234     pnmarith -multiply $tmp_ppm1 $tmp_ppm2 > $tmp_ppm3
235     pnmflip -tb $tmp_ppm3 | ppmnorm > $tmp_ppm2
236     pnmarith -multiply $tmp_ppm1 $tmp_ppm2
237
238   elif [ $N = 6 ]; then
239     N2=`rand 3`
240     if [ $N2 = 0 ]; then
241       pnmflip -lr $tmp_ppm1 > $tmp_ppm2
242     elif [ $N2 = 1 ]; then
243       pnmflip -tb $tmp_ppm1 > $tmp_ppm2
244     else
245       pnmflip -lr $tmp_ppm1 > $tmp_ppm2
246       pnmflip -tb $tmp_ppm2 > $tmp_ppm3
247       cp $tmp_ppm3 $tmp_ppm2
248     fi
249
250     pnmarith -difference $tmp_ppm1 $tmp_ppm2
251
252   elif [ $N = 7 ]; then
253
254     for i in 1 2 3 ; do
255       ppmtopgm $tmp_ppm1 | pgmedge > $tmp_ppm2
256       pnmarith -difference $tmp_ppm1 $tmp_ppm2 > $tmp_ppm3
257       cp $tmp_ppm3 $tmp_ppm1
258     done
259     ppmnorm < $tmp_ppm1
260
261   elif [ $N = 8 ]; then
262     pnmflip -lr $tmp_ppm1 > $tmp_ppm2
263     pnmarith -multiply $tmp_ppm1 $tmp_ppm2 | ppmrelief | ppmnorm | pnminvert
264
265   elif [ $N = 9 ]; then
266     pnmflip -lr $tmp_ppm1 > $tmp_ppm2
267     pnmarith -subtract $tmp_ppm1 $tmp_ppm2 | ppmrelief | ppmtopgm | pgmedge
268
269   elif [ $N = 10 ]; then
270     ppmtopgm $tmp_ppm1 | pgmbentley | pgmtoppm `randcolor`
271
272   elif [ $N = 11 ]; then
273     pgmcrater -number 20000 -height $height -width $width | pgmtoppm `randcolor` > $tmp_ppm2
274     pnmarith -difference $tmp_ppm1 $tmp_ppm2 > $tmp_ppm3
275     pnmflip -tb $tmp_ppm3 | ppmnorm > $tmp_ppm2
276     pnmarith -multiply $tmp_ppm1 $tmp_ppm2
277
278   elif [ $N = 12 ]; then
279     ppmshift 30 $tmp_ppm1 | ppmtopgm | pgmoil | pgmedge |  pgmtoppm `randcolor` > $tmp_ppm2
280     pnmarith -difference $tmp_ppm1 $tmp_ppm2 
281
282  elif [ $N = 13 ]; then
283     ppmpat -madras $width $height | pnmdepth 255 > $tmp_ppm2
284     pnmarith -difference $tmp_ppm1 $tmp_ppm2
285  
286   elif [ $N = 14 ]; then
287     ppmpat -tartan $width $height | pnmdepth 255 > $tmp_ppm2
288     pnmarith -difference  $tmp_ppm1 $tmp_ppm2 
289   
290   elif [ $N = 15 ]; then
291     ppmpat -camo $width $height | pnmdepth 255 | ppmshift 50 > $tmp_ppm2
292     pnmarith -multiply $tmp_ppm1 $tmp_ppm2
293   
294   elif [ $N = 16 ]; then
295     pgmnoise $width $height | pgmedge | pgmtoppm `randcolor` > $tmp_ppm2
296     pnmarith -difference $tmp_ppm1 $tmp_ppm2 | pnmdepth 255 | pnmsmooth
297
298   else cat $tmp_ppm1
299   fi
300 }
301
302
303 # Grab a frame and frob it.  leave it in $tmp_ppm3.
304 #
305 whack() {
306   clean
307
308   while [ ! -f $tmp_ppm1 ]; do
309     if [ "$use_stdin" != true ]; then
310       grab
311     else
312       cp $tmp_ppmS $tmp_ppm0
313       cp $tmp_ppm0 $tmp_ppm1
314     fi
315   done
316
317   rm -f $tmp_rgb
318
319   if [ "$screen_width" != "" ]; then
320     frob | pnmscale -width $screen_width > $tmp_ppm3
321   else
322     frob > $tmp_ppm3
323   fi
324
325   rm -f $tmp_ppm1 $tmp_ppm2
326 }
327
328
329 # Kill off the xli or xv subprocess, if it's running
330 #
331 kill_pid() {
332   if [ "$pid" != "" ]; then
333
334     if [ "$verbose" = true ]; then
335       echo "killing pid $pid..." >&2
336     fi
337
338     # need to do this to avoid "6898 Terminated" messages!
339     # apparently one can't redirect the output of the builtin `kill' command.
340 #    ( sh -c "kill $pid" ) >/dev/null 2>/dev/null </dev/null
341
342     # wtf?  that doesn't work either.  Is it writing to /dev/tty??
343     kill $pid >/dev/null 2>&1
344
345     pid=""
346   fi
347 }
348
349 # called when this process is signalled (for cleanup)
350 #
351 my_trap() {
352   if [ "$verbose" = true ]; then
353     echo "trapped signal!" >&2
354   fi
355   kill_pid
356   clean2
357   exit 1
358 }
359
360 main() {
361
362   getargs $@
363
364   trap my_trap 1 2 3 6 9 13 15
365
366   if [ "$use_stdin" = true ]; then
367    cat > $tmp_ppmS
368   fi
369
370   while true; do
371
372     # Loop grabbing and frobbing images.
373     #
374     # If we're running on the root, run xv or xli in the foreground
375     # (with -exit, if xv) and then wait.
376     #
377     # If we're running in a window, spawn xv or xli in the background; then
378     # when it's time to put up the new image, kill off the currently-running
379     # xv or xli.
380
381     if [ "$verbose" = true ]; then
382       whack
383     else
384       whack >/dev/null 2>&1
385     fi
386
387     kill_pid
388
389     if [ ! -s $tmp_ppm3 ]; then
390       echo "$0: no image grabbed" >&2
391
392     elif [ "$use_stdout" = true ]; then
393
394       cat $tmp_ppm3
395       clean2
396       exit 0
397
398     else
399
400 #      pnmtosgi < $tmp_ppm3 > $tmp_ppm2
401 #      rm -f $tmp_ppm3
402        mv $tmp_ppm3 $tmp_ppm2
403
404       if [ -s $tmp_ppm2 ]; then
405         if [ "$verbose" = true ]; then
406           echo "launching $displayer $displayer_args $tmp_ppm2" >&2
407           ls -lF $tmp_ppm2
408         fi
409
410         mv $tmp_ppm2 $tmp_ppm0
411         eval "$displayer $displayer_args $tmp_ppm0 &"
412
413 # this doesn't work -- leaves xv processes around, instead of stray xset
414 # data.  Sigh.
415 #
416 #       # cat the file so that we can nuke it without racing against xv.
417 #        cat $tmp_ppm2 | $displayer $displayer_args - &
418
419         pid=$!
420       fi
421     fi
422
423     clean
424     sleep $delay
425
426   done
427   exit 1
428 }
429
430 main $@
431
432 # to find stray xv data:
433 # xwininfo -root -children|grep 'xv image comments' | awk '{print "xkill -id ", $1}'