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