7bbc4079b920fbadd506fa1d78107ad44e8fed9e
[xscreensaver] / hacks / vidwhacker
1 #!/bin/sh
2 #
3 # vidwhacker, for xscreensaver.  Copyright (c) 1998 Jamie Zawinski.
4 #
5 # This script grabs a frame of video, then uses various pbm filters to
6 # munge the image in random nefarious ways, then uses xv to put it on
7 # the root window.  This works out really nicely if you just feed some
8 # random TV station into it...
9 #
10 # The video grabbing part is SGI-specific -- if you want to use this on
11 # another system, add a new clause to the grab() procedure.
12
13
14 # Process command-line args...
15
16 onroot=false
17 verbose=false
18 delay=3
19 use_stdin=false
20
21 pid=""
22 tmp=/tmp/vd$$
23 tmp_rgb=$tmp-00000.rgb
24 tmp_ppm0=$tmp-0.ppm
25 tmp_ppm1=$tmp-1.ppm
26 tmp_ppm2=$tmp-2.ppm
27 tmp_ppm3=$tmp-3.ppm
28 tmp_ppm4=$tmp-4.ppm
29
30
31 getargs() {
32
33   while [ $# != 0 ]; do
34     case "$1" in
35     -root )
36       onroot=true
37       ;;
38     -verbose )
39       verbose=true
40       ;;
41     -stdin )
42       use_stdin=true
43       ;;
44     * )
45       echo "usage: $0 [ -root | -verbose | -stdin ]" >&2
46       exit 1
47       ;;
48     esac
49     shift
50   done
51
52   xvargs="-quick24"
53
54   if [ "$onroot" = true ]; then
55     xvargs="$xvargs -root -rmode 5 -noresetroot -rfg black -rbg black -viewonly"
56   else
57     xvargs="$xvargs -geom +0+0"
58   fi
59
60   screen_width=`xdpyinfo | sed -n 's/.* dimensions: *\([0-9]*\).*/\1/p'`
61 }
62
63
64 clean() {
65   rm -f $tmp_rgb $tmp_ppm1 $tmp_ppm2 $tmp_ppm3 $tmp_ppm4
66 }
67
68 clean2() {
69   clean
70   rm -f $tmp_ppm0
71 }
72
73
74 # Grab a frame of video.
75 #
76 grab() {
77   uname=`uname`
78   if [ $uname = IRIX ]; then
79     #
80     # SGI's "vidtomem" returns an SGI RGB image of the default video input,
81     # and has stupid non-overridable ouput-file-naming conventions.  So, let 
82     # it write its file; and then convert it to a pgm.
83     #
84     
85     vidtomem -f $tmp
86     sgitopnm $tmp_rgb > $tmp_ppm1
87
88     # Cut off the close-captioning blips in the NTSC overscan region.  YMMV.
89     #  | pnmcut 12 7 695 477 
90
91   elif [ $uname = Linux ]; then
92
93     # Marcus Herbert says the following works with his Connectix Qcam.
94     # Don't have qcam?  Well, do something else then...  and send me a patch.
95
96     qcam > $tmp_ppm1
97
98   else
99     echo "$0: don't know how to grab video on this OS." >&2
100     clean2
101     exit 1
102   fi
103 }
104
105
106 # Use perl to pick a random foreground/background color in pbm's syntax.
107 #
108 randcolor() {
109   perl -e 'srand; 
110            printf("#%02x%02x%02x-#%02x%02x%02x",
111                   int(rand()*60),
112                   int(rand()*60),
113                   int(rand()*60),
114                   120+int(rand()*135),
115                   120+int(rand()*135),
116                   120+int(rand()*135))'
117 }
118
119 # Frobnicate the image in some random way.
120 #
121 frob() {
122
123   w_h=`head -2 $tmp_ppm1 | tail -1`
124   width=`echo $w_h | awk '{print $1}'`
125   height=`echo $w_h | awk '{print $2}'`
126
127   N=`perl -e 'srand; print int(rand() * 17)'`
128
129   if [ "$verbose" = true ]; then
130     echo "mode $N..." >&2
131   fi
132
133   if   [ $N = 0 ]; then
134     ppmtopgm $tmp_ppm1 | pgmedge | pgmtoppm `randcolor` | ppmnorm
135
136   elif [ $N = 1 ]; then
137     ppmtopgm $tmp_ppm1 | 
138     pgmenhance | 
139     pgmtoppm `randcolor`
140
141   elif [ $N = 2 ]; then
142     ppmtopgm $tmp_ppm1 | pgmoil | pgmtoppm `randcolor`
143
144   elif [ $N = 3 ]; then 
145     ppmrelief $tmp_ppm1 | ppmtopgm | pgmedge | ppmrelief | ppmtopgm |
146       pgmedge | pnminvert | pgmtoppm `randcolor`
147
148   elif [ $N = 4 ]; then
149     ppmspread 71 $tmp_ppm1 > $tmp_ppm2
150     pnmarith -add $tmp_ppm1 $tmp_ppm2
151
152   elif [ $N = 5 ]; then
153     pnmflip -lr $tmp_ppm1 > $tmp_ppm2
154     pnmarith -multiply $tmp_ppm1 $tmp_ppm2 > $tmp_ppm3
155     pnmflip -tb $tmp_ppm3 | ppmnorm > $tmp_ppm2
156     pnmarith -multiply $tmp_ppm1 $tmp_ppm2
157
158   elif [ $N = 6 ]; then
159     N2=`perl -e 'srand; print int(rand() * 3)'`
160     if [ $N2 = 0 ]; then
161       pnmflip -lr $tmp_ppm1 > $tmp_ppm2
162     elif [ $N2 = 1 ]; then
163       pnmflip -tb $tmp_ppm1 > $tmp_ppm2
164     else
165       pnmflip -lr $tmp_ppm1 > $tmp_ppm2
166       pnmflip -tb $tmp_ppm2 > $tmp_ppm3
167       cp $tmp_ppm3 $tmp_ppm2
168     fi
169
170     pnmarith -difference $tmp_ppm1 $tmp_ppm2
171
172   elif [ $N = 7 ]; then
173
174     for i in 1 2 3 ; do
175       ppmtopgm $tmp_ppm1 | pgmedge > $tmp_ppm2
176       pnmarith -difference $tmp_ppm1 $tmp_ppm2 > $tmp_ppm3
177       cp $tmp_ppm3 $tmp_ppm1
178     done
179     ppmnorm < $tmp_ppm1
180
181   elif [ $N = 8 ]; then
182     pnmflip -lr $tmp_ppm1 > $tmp_ppm2
183     pnmarith -multiply $tmp_ppm1 $tmp_ppm2 | ppmrelief | ppmnorm | pnminvert
184
185   elif [ $N = 9 ]; then
186     pnmflip -lr $tmp_ppm1 > $tmp_ppm2
187     pnmarith -subtract $tmp_ppm1 $tmp_ppm2 | ppmrelief | ppmtopgm | pgmedge
188
189   elif [ $N = 10 ]; then
190     ppmtopgm $tmp_ppm1 | pgmbentley | pgmtoppm `randcolor`
191
192   elif [ $N = 11 ]; then
193     pgmcrater -number 20000 -height $height -width $width | pgmtoppm `randcolor` > $tmp_ppm2
194     pnmarith -difference $tmp_ppm1 $tmp_ppm2 > $tmp_ppm3
195     pnmflip -tb $tmp_ppm3 | ppmnorm > $tmp_ppm2
196     pnmarith -multiply $tmp_ppm1 $tmp_ppm2
197
198   elif [ $N = 12 ]; then
199     ppmshift 30 $tmp_ppm1 | ppmtopgm | pgmoil | pgmedge |  pgmtoppm `randcolor` > $tmp_ppm2
200     pnmarith -difference $tmp_ppm1 $tmp_ppm2 
201
202  elif [ $N = 13 ]; then
203     ppmpat -madras $width $height | pnmdepth 255 > $tmp_ppm2
204     pnmarith -difference $tmp_ppm1 $tmp_ppm2
205  
206   elif [ $N = 14 ]; then
207     ppmpat -tartan $width $height | pnmdepth 255 > $tmp_ppm2
208     pnmarith -difference  $tmp_ppm1 $tmp_ppm2 
209   
210   elif [ $N = 15 ]; then
211     ppmpat -camo $width $height | pnmdepth 255 | ppmshift 50 > $tmp_ppm2
212     pnmarith -multiply $tmp_ppm1 $tmp_ppm2
213   
214   elif [ $N = 16 ]; then
215     pgmnoise $width $height | pgmedge | pgmtoppm `randcolor` > $tmp_ppm2
216     pnmarith -difference $tmp_ppm1 $tmp_ppm2 | pnmdepth 255 | pnmsmooth
217
218   else cat $tmp_ppm1
219   fi
220 }
221
222
223 # Grab a frame and frob it.  leave it in $tmp_ppm3.
224 #
225 whack() {
226   clean
227
228   while [ ! -f $tmp_ppm1 ]; do
229     if [ "$use_stdin" != true ]; then
230       grab
231     else
232       cp $tmp_ppm0 $tmp_ppm1
233     fi
234   done
235
236   rm -f $tmp_rgb
237   frob | pnmscale -width $screen_width > $tmp_ppm3
238   rm -f $tmp_ppm1 $tmp_ppm2
239 }
240
241
242 # Kill off the xv subprocess, if it's running
243 #
244 kill_pid() {
245   if [ "$pid" != "" ]; then
246
247     if [ "$verbose" = true ]; then
248       echo "killing pid $pid..." >&2
249     fi
250
251     # need to do this to avoid "6898 Terminated" messages!
252     # apparently one can't redirect the output of the builtin `kill' command.
253 #    ( sh -c "kill $pid" ) >/dev/null 2>/dev/null </dev/null
254
255     # wtf?  that doesn't work either.  Is it writing to /dev/tty??
256     kill $pid >&- >&-
257
258     pid=""
259   fi
260 }
261
262 # called when this process is signalled (for cleanup)
263 #
264 my_trap() {
265   if [ "$verbose" = true ]; then
266     echo "trapped signal!" >&2
267   fi
268   kill_pid
269   clean2
270   exit 1
271 }
272
273 main() {
274
275   getargs $@
276
277   trap my_trap 0 1 2 3 6 9 13
278
279   if [ "$use_stdin" = true ]; then
280    cat > $tmp_ppm0
281   fi
282
283   while true; do
284
285     # Loop grabbing and frobbing images.
286     #
287     # If we're running on the root, run xv in the foreground (with -exit)
288     # and then wait.
289     #
290     # If we're running in a window, spawn xv in the background; then when
291     # it's time to put up the new image, kill off the currently-running xv.
292
293     if [ "$verbose" = true ]; then
294       whack
295     else
296       whack >&- 2>&-
297     fi
298
299     kill_pid
300
301     if [ ! -s $tmp_ppm3 ]; then
302       echo "$0: no image grabbed" >&2
303     else
304
305       pnmtosgi < $tmp_ppm3 > $tmp_ppm2
306       rm -f $tmp_ppm3
307
308       if [ -s $tmp_ppm2 ]; then
309         if [ "$verbose" = true ]; then
310           echo "launching xv $xvargs $tmp_ppm2" >&2
311         fi
312         xv $xvargs $tmp_ppm2 &
313         pid=$!
314       fi
315     fi
316
317     clean
318     sleep $delay
319
320   done
321   exit 1
322 }
323
324 main $@
325
326 # to find stray xv data:
327 # xwininfo -root -children|grep 'xv image comments' | awk '{print $1}'