http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / xanalogtv.c
1 /* xanalogtv, Copyright (c) 2003 Trevor Blackwell <tlb@tlb.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  *
12  * Simulate test patterns on an analog TV. Concept similar to xteevee
13  * in this distribution, but a totally different implementation based
14  * on the simulation of an analog TV set in utils/analogtv.c. Much
15  * more realistic, but needs more video card bandwidth.
16  *
17  * It flips around through simulated channels 2 through 13. Some show
18  * pictures from your images directory, some show color bars, and some
19  * just have static. Some channels receive two stations simultaneously
20  * so you see a ghostly, misaligned image.
21  *
22  * It's easy to add some test patterns by compiling in an XPM, but I
23  * can't find any that are clearly freely redistributable.
24  *
25  */
26
27 #include <math.h>
28 #include "screenhack.h"
29 #include "xpm-pixmap.h"
30 #include "analogtv.h"
31 #include <stdio.h>
32 #include <time.h>
33 #include <sys/time.h>
34 #include <X11/Xutil.h>
35 #include <X11/Intrinsic.h>
36
37 #include "images/logo-50.xpm"
38
39 /* #define DEBUG 1 */
40 /* #define USE_TEST_PATTERNS */
41
42 #define countof(x) (sizeof((x))/sizeof((*x)))
43
44 static analogtv *tv=NULL;
45
46 analogtv_font ugly_font;
47
48 static void
49 update_smpte_colorbars(analogtv_input *input)
50 {
51   int col;
52   int xpos, ypos;
53   int black_ntsc[4];
54
55   /* 
56      SMPTE is the society of motion picture and television engineers, and
57      these are the standard color bars in the US. Following the partial spec
58      at http://broadcastengineering.com/ar/broadcasting_inside_color_bars/
59      These are luma, chroma, and phase numbers for each of the 7 bars.
60   */
61   double top_cb_table[7][3]={
62     {75, 0, 0.0},    /* gray */
63     {69, 31, 167.0}, /* yellow */
64     {56, 44, 283.5}, /* cyan */
65     {48, 41, 240.5}, /* green */
66     {36, 41, 60.5},  /* magenta */
67     {28, 44, 103.5}, /* red */
68     {15, 31, 347.0}  /* blue */
69   };
70   double mid_cb_table[7][3]={
71     {15, 31, 347.0}, /* blue */
72     {7, 0, 0},       /* black */
73     {36, 41, 60.5},  /* magenta */
74     {7, 0, 0},       /* black */
75     {56, 44, 283.5}, /* cyan */
76     {7, 0, 0},       /* black */
77     {75, 0, 0.0}     /* gray */
78   };
79
80   analogtv_lcp_to_ntsc(0.0, 0.0, 0.0, black_ntsc);
81
82   analogtv_setup_sync(input, 1, 0);
83   analogtv_setup_teletext(input);
84
85   for (col=0; col<7; col++) {
86     analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.00, 0.68, 
87                                 top_cb_table[col][0], 
88                                 top_cb_table[col][1], top_cb_table[col][2]);
89     
90     analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.68, 0.75, 
91                                 mid_cb_table[col][0], 
92                                 mid_cb_table[col][1], mid_cb_table[col][2]);
93   }
94
95   analogtv_draw_solid_rel_lcp(input, 0.0, 1.0/6.0,
96                               0.75, 1.00, 7, 40, 303);   /* -I */
97   analogtv_draw_solid_rel_lcp(input, 1.0/6.0, 2.0/6.0,
98                               0.75, 1.00, 100, 0, 0);    /* white */
99   analogtv_draw_solid_rel_lcp(input, 2.0/6.0, 3.0/6.0,
100                               0.75, 1.00, 7, 40, 33);    /* +Q */
101   analogtv_draw_solid_rel_lcp(input, 3.0/6.0, 4.0/6.0,
102                               0.75, 1.00, 7, 0, 0);      /* black */
103   analogtv_draw_solid_rel_lcp(input, 12.0/18.0, 13.0/18.0,
104                               0.75, 1.00, 3, 0, 0);      /* black -4 */
105   analogtv_draw_solid_rel_lcp(input, 13.0/18.0, 14.0/18.0,
106                               0.75, 1.00, 7, 0, 0);      /* black */
107   analogtv_draw_solid_rel_lcp(input, 14.0/18.0, 15.0/18.0,
108                               0.75, 1.00, 11, 0, 0);     /* black +4 */
109   analogtv_draw_solid_rel_lcp(input, 5.0/6.0, 6.0/6.0,
110                               0.75, 1.00, 7, 0, 0);      /* black */
111
112
113   ypos=ANALOGTV_V/5;
114   xpos=ANALOGTV_VIS_START + ANALOGTV_VIS_LEN/2;
115
116   {
117     char localname[256];
118     if (gethostname (localname, sizeof (localname))==0) {
119       localname[sizeof(localname)-1]=0; /* "The returned name is null-
120                                            terminated unless insufficient 
121                                            space is provided" */
122       localname[24]=0; /* limit length */
123
124       analogtv_draw_string_centered(input, &ugly_font, localname,
125                                     xpos, ypos, black_ntsc);
126     }
127   }
128   ypos += ugly_font.char_h*5/2;
129
130   analogtv_draw_xpm(tv, input,
131                     logo_50_xpm, xpos - 100, ypos);
132
133   ypos += 58;
134
135 #if 0
136   analogtv_draw_string_centered(input, &ugly_font, "Please Stand By", xpos, ypos);
137   ypos += ugly_font.char_h*4;
138 #endif
139
140   {
141     char timestamp[256];
142     time_t t = time ((time_t *) 0);
143     struct tm *tm = localtime (&t);
144
145     /* Y2K: It is OK for this to use a 2-digit year because it's simulating a
146        TV display and is purely decorative. */
147     strftime(timestamp, sizeof(timestamp)-1, "%y.%m.%d %H:%M:%S ", tm);
148     analogtv_draw_string_centered(input, &ugly_font, timestamp,
149                                   xpos, ypos, black_ntsc);
150   }
151
152   
153   input->next_update_time += 1.0;
154 }
155
156 #if 0
157 static void
158 draw_color_square(analogtv_input *input)
159 {
160   double xs,ys;
161
162   analogtv_draw_solid_rel_lcp(input, 0.0, 1.0, 0.0, 1.0,
163                               30.0, 0.0, 0.0);
164   
165   for (xs=0.0; xs<0.9999; xs+=1.0/15.0) {
166     analogtv_draw_solid_rel_lcp(input, xs, xs, 0.0, 1.0,
167                                 100.0, 0.0, 0.0);
168   }
169
170   for (ys=0.0; ys<0.9999; ys+=1.0/11.0) {
171     analogtv_draw_solid_rel_lcp(input, 0.0, 1.0, ys, ys,
172                                 100.0, 0.0, 0.0);
173   }
174
175   for (ys=0.0; ys<0.9999; ys+=0.01) {
176     
177     analogtv_draw_solid_rel_lcp(input, 0.0/15, 1.0/15, ys, ys+0.01,
178                                 40.0, 45.0, 103.5*(1.0-ys) + 347.0*ys);
179
180     analogtv_draw_solid_rel_lcp(input, 14.0/15, 15.0/15, ys, ys+0.01,
181                                 40.0, 45.0, 103.5*(ys) + 347.0*(1.0-ys));
182   }
183
184   for (ys=0.0; ys<0.9999; ys+=0.02) {
185     analogtv_draw_solid_rel_lcp(input, 1.0/15, 2.0/15, ys*2.0/11.0+1.0/11.0, 
186                                 (ys+0.01)*2.0/11.0+1.0/11.0,
187                                 100.0*(1.0-ys), 0.0, 0.0);
188   }
189
190
191 }
192 #endif
193
194 char *progclass = "XAnalogTV";
195
196 char *defaults [] = {
197   "*delay:           5",
198   ANALOGTV_DEFAULTS
199   0,
200 };
201
202 XrmOptionDescRec options [] = {
203   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
204   ANALOGTV_OPTIONS
205   { 0, 0, 0, 0 }
206 };
207
208
209 #ifdef USE_TEST_PATTERNS
210
211 #include "images/earth.xpm"
212
213 char **test_patterns[] = {
214   earth_xpm,
215 };
216
217 #endif
218
219
220 enum {
221   N_CHANNELS=12, /* Channels 2 through 13 on VHF */
222   MAX_MULTICHAN=2
223 }; 
224
225 typedef struct chansetting_s {
226
227   analogtv_reception recs[MAX_MULTICHAN];
228   double noise_level;
229
230   int dur;
231 } chansetting;
232
233 static struct timeval basetime;
234
235 static int
236 getticks(void)
237 {
238   struct timeval tv;
239   gettimeofday(&tv,NULL);
240   return ((tv.tv_sec - basetime.tv_sec)*1000 +
241           (tv.tv_usec - basetime.tv_usec)/1000);
242 }
243
244 int
245 analogtv_load_random_image(analogtv *it, analogtv_input *input)
246 {
247   Pixmap pixmap;
248   XImage *image=NULL;
249   int width=ANALOGTV_PIC_LEN;
250   int height=width*3/4;
251   int rc;
252
253   pixmap=XCreatePixmap(it->dpy, it->window, width, height, it->visdepth);
254   XSync(it->dpy, False);
255   load_random_image(it->screen, it->window, pixmap, NULL);
256   image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
257   XFreePixmap(it->dpy, pixmap);
258
259   /* Make sure the window's background is not set to None, and get the
260      grabbed bits (if any) off it as soon as possible. */
261   XSetWindowBackground (it->dpy, it->window,
262                         get_pixel_resource ("background", "Background",
263                                             it->dpy, it->xgwa.colormap));
264   XClearWindow (it->dpy, it->window);
265
266   analogtv_setup_sync(input, 1, (random()%20)==0);
267   rc=analogtv_load_ximage(it, input, image);
268   if (image) XDestroyImage(image);
269   XSync(it->dpy, False);
270   return rc;
271 }
272
273 int
274 analogtv_load_xpm(analogtv *it, analogtv_input *input, char **xpm)
275 {
276   Pixmap pixmap;
277   XImage *image;
278   int width,height;
279   int rc;
280
281   pixmap=xpm_data_to_pixmap (it->dpy, it->window, xpm,
282                              &width, &height, NULL);
283   image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
284   XFreePixmap(it->dpy, pixmap);
285   rc=analogtv_load_ximage(it, input, image);
286   if (image) XDestroyImage(image);
287   XSync(it->dpy, False);
288   return rc;
289 }
290
291 enum { MAX_STATIONS = 6 };
292 static int n_stations;
293 static analogtv_input *stations[MAX_STATIONS];
294
295
296 void add_stations(void)
297 {
298   while (n_stations < MAX_STATIONS) {
299     analogtv_input *input=analogtv_input_allocate();
300     stations[n_stations++]=input;
301
302     if (n_stations==1) {
303       input->updater = update_smpte_colorbars;
304       input->do_teletext=1;
305     }
306 #ifdef USE_TEST_PATTERNS
307     else if (random()%5==0) {
308       j=random()%countof(test_patterns);
309       analogtv_setup_sync(input);
310       analogtv_load_xpm(tv, input, test_patterns[j]);
311       analogtv_setup_teletext(input);
312     }
313 #endif
314     else {
315       analogtv_load_random_image(tv, input);
316       input->do_teletext=1;
317     }
318   }
319 }
320
321 void
322 screenhack (Display *dpy, Window window)
323 {
324   int i;
325   int curinputi;
326   int change_ticks;
327   int using_mouse=0;
328   int change_now;
329   chansetting chansettings[N_CHANNELS];
330   chansetting *cs;
331   int last_station=42;
332   int delay = get_integer_resource("delay", "Integer");
333   if (delay < 1) delay = 1;
334
335   analogtv_make_font(dpy, window, &ugly_font, 7, 10, "6x10");
336   
337   tv=analogtv_allocate(dpy, window);
338   tv->event_handler = screenhack_handle_event;
339
340   add_stations();
341
342   analogtv_set_defaults(tv, "");
343   tv->need_clear=1;
344
345   if (random()%4==0) {
346     tv->tint_control += pow(frand(2.0)-1.0, 7) * 180.0;
347   }
348   if (1) {
349     tv->color_control += frand(0.3);
350   }
351
352   for (i=0; i<N_CHANNELS; i++) {
353     memset(&chansettings[i], 0, sizeof(chansetting));
354
355     chansettings[i].noise_level = 0.06;
356     chansettings[i].dur = 1000*delay;
357
358     if (random()%6==0) {
359       chansettings[i].dur=600;
360     }
361     else {
362       int stati;
363       for (stati=0; stati<MAX_MULTICHAN; stati++) {
364         analogtv_reception *rec=&chansettings[i].recs[stati];
365         int station;
366         while (1) {
367           station=random()%n_stations;
368           if (station!=last_station) break;
369           if ((random()%10)==0) break;
370         }
371         last_station=station;
372         rec->input = stations[station];
373         rec->level = pow(frand(1.0), 3.0) * 2.0 + 0.05;
374         rec->ofs=random()%ANALOGTV_SIGNAL_LEN;
375         if (random()%3) {
376           rec->multipath = frand(1.0);
377         } else {
378           rec->multipath=0.0;
379         }
380         if (stati) {
381           /* We only set a frequency error for ghosting stations,
382              because it doesn't matter otherwise */
383           rec->freqerr = (frand(2.0)-1.0) * 3.0;
384         }
385
386         if (rec->level > 0.3) break;
387         if (random()%4) break;
388       }
389     }
390   }
391
392   gettimeofday(&basetime,NULL);
393
394   curinputi=0;
395   cs=&chansettings[curinputi];
396   change_ticks = cs->dur + 1500;
397
398   tv->powerup=0.0;
399   while (1) {
400     int curticks=getticks();
401     double curtime=curticks*0.001;
402
403     change_now=0;
404     if (analogtv_handle_events(tv)) {
405       using_mouse=1;
406       change_now=1;
407     }
408     if (change_now || (!using_mouse && curticks>=change_ticks 
409                        && tv->powerup > 10.0)) {
410       curinputi=(curinputi+1)%N_CHANNELS;
411       cs=&chansettings[curinputi];
412       change_ticks = curticks + cs->dur;
413       /* Set channel change noise flag */
414       tv->channel_change_cycles=200000;
415     }
416
417     for (i=0; i<MAX_MULTICHAN; i++) {
418       analogtv_reception *rec=&cs->recs[i];
419       analogtv_input *inp=rec->input;
420       if (!inp) continue;
421
422       if (inp->updater) {
423         inp->next_update_time = curtime;
424         (inp->updater)(inp);
425       }
426       rec->ofs += rec->freqerr;
427     }
428
429     tv->powerup=curtime;
430
431     analogtv_init_signal(tv, cs->noise_level);
432     for (i=0; i<MAX_MULTICHAN; i++) {
433       analogtv_reception *rec=&cs->recs[i];
434       analogtv_input *inp=rec->input;
435       if (!inp) continue;
436
437       analogtv_reception_update(rec);
438       analogtv_add_signal(tv, rec);
439     }
440     analogtv_draw(tv);
441   }
442
443   XSync(dpy, False);
444   XClearWindow(dpy, window);
445   
446   if (tv) analogtv_release(tv);
447 }
448