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