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