source: trunk/third/xscreensaver/hacks/xanalogtv.c @ 20148

Revision 20148, 12.2 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
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
44static analogtv *tv=NULL;
45
46analogtv_font ugly_font;
47
48static void
49update_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
157static void
158draw_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
194char *progclass = "XAnalogTV";
195
196char *defaults [] = {
197  "*delay:           5",
198  ANALOGTV_DEFAULTS
199  0,
200};
201
202XrmOptionDescRec 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
213char **test_patterns[] = {
214  earth_xpm,
215};
216
217#endif
218
219
220enum {
221  N_CHANNELS=12, /* Channels 2 through 13 on VHF */
222  MAX_MULTICHAN=2
223};
224
225typedef struct chansetting_s {
226
227  analogtv_reception recs[MAX_MULTICHAN];
228  double noise_level;
229
230  int dur;
231} chansetting;
232
233static struct timeval basetime;
234
235static int
236getticks(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
244int
245analogtv_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
273int
274analogtv_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
291enum { MAX_STATIONS = 6 };
292static int n_stations;
293static analogtv_input *stations[MAX_STATIONS];
294
295
296void 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
321void
322screenhack (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
Note: See TracBrowser for help on using the repository browser.